题意:
无向图G初始有n个点,从1到n依次标号,但是没有边
接下来有m次操作,从1到m依次标号,你需要对每种操作输出相应的结果,操作分为两种:
0 u v:加入边(u,v)。输出加边后图G中连通块的个数。
1 u v:查询(u,v)是否连通。如果连通输出k,表示最早在第k次操作后标号为u和标号为v的点之间连通,否则输出0
Small:n≤103,m≤5×103
Large:n≤105,m≤5×105
分析:
Small:并查集维护连通块个数,如果可以合并,则连通块数−1
对于输出第k次操作连通,只要把操作记录下来,再开一个并查集来尝试加边操作,直到连通为止
复杂度为O(nm)
代码:
//
// Created by TaoSama on 2016-02-03
// Copyright (c) 2015 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#include <cassert>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
struct DSU {
int n, p[N], r[N];
void init(int _n) {
n = _n;
for(int i = 1; i <= n; ++i) p[i] = i, r[i] = 0;
}
int find(int x) {
return p[x] = p[x] == x ? x : find(p[x]);
}
bool unite(int x, int y) {
x = find(x), y = find(y);
if(x == y) return false;
if(r[x] < r[y]) p[x] = y;
else {
p[y] = x;
if(r[x] == r[y]) ++r[x];
}
return true;
}
bool same(int x, int y) {
x = find(x), y = find(y);
return x == y;
}
} dsu, tmp;
int n, m;
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
dsu.init(n);
int last = 0, ans = n;
vector<pair<pair<int, int>, int> > Q;
for(int q = 1; q <= m; ++q) {
int op, u, v; scanf("%d%d%d", &op, &u, &v);
u ^= last, v ^= last;
// printf("op.... %d %d %d\n", op, u, v);
if(op == 0) {
Q.push_back(make_pair(make_pair(u, v), q));
ans -= dsu.unite(u, v);
printf("%d\n", ans);
last = ans;
} else {
int ret = dsu.same(u, v);
if(ret) {
tmp.init(n);
for(int i = 0; i < Q.size(); ++i) {
int x = Q[i].first.first, y = Q[i].first.second;
int k = Q[i].second;
tmp.unite(x, y);
if(tmp.same(u, v)) {
ret = k;
break;
}
}
}
printf("%d\n", ret);
last = ret;
}
}
}
return 0;
}
Large:如果使用可持久化并查集,二分答案判定连通性,复杂度是O(mlog3n),不能在时限内出解
考虑到并查集实际上是一棵树,可以尝试在边上维护一些信息
假设t时刻加了一条边(u,v),若u和v此时未连通,则在root(u)和root(v)之间连一条权值为t的边,表示u所在集合以及v所在集合在t时刻连通
需要注意的是,我们将边权映射到点上,是放在深度大的那个点上,也就是树高小的那个根
这样对于一组查询(u,v),如果u和v位于同一个连通块内,只需找出并查集中u到v的路径上的权值最大值
很显然这样是不能路径压缩的,但是可以按秩合并保证树高是O(logn),总的复杂度是O(mlogn)
代码:
//
// Created by TaoSama on 2016-02-03
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, m;
int p[N], r[N], id[N];
int find(int x) {
return p[x] == x ? x : find(p[x]);
}
int unite(int x, int y, int q) {
x = find(x), y = find(y);
if(x == y) return false;
if(r[x] > r[y]) swap(x, y);
p[x] = y;
id[x] = q;
if(r[x] == r[y]) ++r[y];
return true;
}
int lca(int u, int v) {
set<int> s;
while(u != p[u]) s.insert(u), u = p[u];
s.insert(u);
while(!s.count(v) && v != p[v]) v = p[v];
if(!s.count(v)) return 0;
return v;
}
int query(int x, int y) {
int _lca = lca(x, y);
// printf("u: %d v: %d lca: %d\n", x, y, _lca);
if(!_lca) return 0;
int ret = 0;
while(x != _lca) ret = max(ret, id[x]), x = p[x];
while(y != _lca) ret = max(ret, id[y]), y = p[y];
return ret;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) p[i] = i, r[i] = 0;
int ans = 0, cc = n;
for(int i = 1; i <= m; ++i) {
int op, x, y; scanf("%d%d%d", &op, &x, &y);
x ^= ans, y ^= ans;
// printf("op..... %d %d %d\n", op, x, y);
if(op == 0) {
cc -= unite(x, y, i);
ans = cc;
} else {
ans = query(x, y);
}
printf("%d\n", ans);
}
}
return 0;
}