标签:
http://poj.openjudge.cn/practice/C15C?lang=en_US
n 点 m 边 k 天。
每条边在某一天会消失(仅仅那一天消失)。
问每一天有多少对点可以相互到达。
这个一看就是并查集。但是呢。。。。
“并查集是不能删边的”,这是这个问题的障碍。
并查集真的不能删边么?其实不是绝对的,如果删除的边是最后加入的边,并且并查集不进行路径压缩的话,自然是可以删除的。
P.S. 并查集找根如果路径压缩加按秩合并的话是 $O(log(log(n)))$,如果只按秩合并的话是 $O(log(n))$。
那么怎么让安排顺序,让并查集可以删除的边都是最后加入的边呢?这就用到了 cdq 分治(P.S. cdq 前辈提出的一类分治)。
考虑处理 1-10 天每天的连通对数目,并且最后将 1-10天的边都正确退出并查集的过程记做 cdq(1,10)。
那么 cdq(1, 10) 有如下的过程:
1. 6-10 的边加入并查集。
2. cdq(1, 5)
3. 6-10 的边退出并查集
4. 1-5 的边加入并查集
5. cdq(6, 10)
6. 1-5 的边退出并查集
注意:
0. 递归一直进行,直到 cdq(i, i) 的状况。这个时候就会发现,当前除了第 i 天的边,其他的边都加在并查集里。
1. 途中维护一个数 result,表示当前并查集中连通对的数目。如果是 cdq(i,i) 的话,其实什么都不用做,只要输出 result 就可以了。
2. 第 3 步删边的时候,应该反第 1 步的加边的顺序。为此,用一个栈存当前合并的集合根对。在 1 的时候记录栈顶位置 tmp,3 的时候反向退栈直到 tmp。 4 与 6 也是类似的。
3. 找根复杂度是 $log(n)$,cdq(l, r) 复杂度是 $O((r-l)log(n))$ 所以总复杂度应该是 $O(klog^2(n))$
另:
1. 一开始没注意顺序,瞎写。
2. Vjudge 说这个题 64 位整数的占位符是 %I64d %I64u,不要信它。
#include <stdio.h>
#include <vector>
#include <algorithm>
using std::vector;
const int MAXN = 100000 + 5;
const int MAXM = 200000 + 5;
int fa[MAXN];
int cnt[MAXN];
void initSets(int n)
{
for (int i = 1; i <= n; i++) {
fa[i] = i;
cnt[i] = 1;
}
}
int root(int i)
{
for (; i - fa[i]; i = fa[i]);
return i;
}
struct Edge
{
int x, y;
};
vector<vector<Edge> > D;
long long c2(int x)
{ return 1LL * x * (x - 1) / 2; }
struct CDQ
{
std::pair<int, int> stk[MAXM];
int stkTop;
long long result;
void push(Edge& now)
{
int a = root(now.x);
int b = root(now.y);
if (a == b)
return;
result -= c2(cnt[a]);
result -= c2(cnt[b]);
if (cnt[a] > cnt[b])
std::swap(a, b);
// a -> b
cnt[b] += cnt[a];
result += c2(cnt[b]);
fa[a] = b;
stkTop++;
stk[stkTop].first = a;
stk[stkTop].second = b;
}
void push(int l, int r)
{
for (int i = l; i <= r; i++)
for (int j = 0; j < D[i].size(); j++)
push(D[i][j]);
}
void pull(int l, int r)
{
for (int i = r; i >= l; i--) {
int s = stk[i].first;
int r = stk[i].second;
result -= c2(cnt[r]);
cnt[r] -= cnt[s];
result += c2(cnt[s]);
result += c2(cnt[r]);
fa[s] = s;
}
}
void cdq(int l, int r)
{
if (l == r) {
printf("%lld\n", result);
return;
}
int mid = (l + r) >> 1;
int tmp = stkTop;
push(mid + 1, r);
cdq(l, mid);
pull(tmp + 1, stkTop);
stkTop = tmp;
push(l, mid);
cdq(mid + 1, r);
pull(tmp + 1, stkTop);
stkTop = tmp;
}
CDQ(int k, long long result)
{
stkTop = 0;
this -> result = result;
cdq(1, k);
}
};
int main()
{
int n, m, k;
for (; scanf("%d %d %d", &n, &m, &k) == 3; ) {
initSets(n);
D.clear();
D.resize(k + 1);
int d;
Edge edge;
long long result = 0;
for (int i = 0; i < m; i++) {
scanf("%d %d %d", &edge.x, &edge.y, &d);
if (d <= k) {
D[d].push_back(edge);
continue;
}
int a = root(edge.x);
int b = root(edge.y);
if (cnt[a] > cnt[b])
std::swap(a, b);
// a -> b
fa[a] = b;
result -= c2(cnt[a]) + c2(cnt[b]);
cnt[b] += cnt[a];
result += c2(cnt[b]);
}
CDQ solve(k, result);
}
return 0;
}
Open_POJ C15C Rabbit's Festival
标签:
原文地址:http://www.cnblogs.com/gu-castle/p/5000120.html