给定d张无向图,每张图都有n个点。一开始,在任何一张图中都没有任何边。接下来有m次操做,每次操做会给出a,b,k,意为在第k张图中的点a和点b之间添加一条无向边。你须要在每次操做以后输出有序数对(a,b)的个数,使得1<=a,b<=n,且a点和b点在d张图中都连通。优化
Input
第一行包含三个正整数d,n,m(1<=d<=200,1<=n<=5000,1<=m<=1000000),依次表示图的个数,点的个数和操做的个数。
接下来m行,每行包含三个正整数a,b,k(1<=a,b<=n,1<=k<=d),依次描述每个操做。spa
Output
输出m行m个正整数,依次表示每次操做以后知足条件的有序数对(a,b)的个数。code
Sample Input
3 4 10
1 2 1
2 1 2
1 2 3
3 4 1
1 3 2
2 3 3
2 4 2
3 4 3
3 4 2
1 3 1
Sample Output
4
4
6
6
6
6
6
8
8
16ip
考虑动态统计增量。
当合并两个连通块时,咱们采用启发式合并的方法,将小的一个个塞进大的里面。在一个个塞的时候,统计新多出来的在 d 张图都连通的点对数量。
由于是启发式合并,每一个点最多被暴力塞 log 次,因此这一部分的复杂度为 O(nlogn)。字符串
怎么判断两个点在 d 张图中都连通呢?由于只有加边,考虑使用并查集。
假如对于点 x 与点 y 在每张图中都有 find(x) = find(y)(find(x) 即 x 所在集合的表明元素),那么在 d 张图中 x, y 都连通。
在求点 x 与多少个点在 d 张图中都连通,即给定一个 find 序列 find(1,x), find(2,x), ..., find(d,x),求有多少个点对应的 find 序列与它相同。get
断定两个序列是否相等,除了逐个比较外还能够使用字符串哈希的方法。
那么就能够经过将 find 序列给哈希了,获得的哈希值拿去找相同的值有多少个。
这个能够再写一个哈希表搞定(注意哈希表 ≠ 字符串哈希,虽然都是哈希。。。)hash
在合并连通块的时候,find 序列只会有一个位置发生改变,改哈希值能够 O(1) 改。it
#include <cstdio> #include <vector> #include <iostream> using namespace std; #define fi first #define se second typedef long long ll; typedef unsigned long long ull; typedef pair<ull, int> hh; const int MAXD = 200; const int MAXN = 5000; const ull BASE = 13131; const int HASHSIZE = 1000037; vector<hh>h[HASHSIZE]; int ans; void hash_insert(ull x) { int hx = x % HASHSIZE; for(int i=0;i<h[hx].size();i++) if( h[hx][i].fi == x ) { ans += 2*h[hx][i].se; h[hx][i].se++; return ; } h[hx].push_back(make_pair(x, 1)); } void hash_erase(ull x) { int hx = x % HASHSIZE; for(int i=0;i<h[hx].size();i++) if( h[hx][i].fi == x ) { h[hx][i].se--; ans -= 2*h[hx][i].se; } } ull pw[MAXD + 5], hsh[MAXN + 5]; vector<int>sn[MAXD + 5][MAXN + 5]; int fa[MAXD + 5][MAXN + 5], rnk[MAXD + 5][MAXN + 5]; int find(int d, int x) { return fa[d][x] = (fa[d][x] == x ? x : find(d, fa[d][x])); } void dfs(int d, int x, ull del) { for(int i=0;i<sn[d][x].size();i++) dfs(d, sn[d][x][i], del); hash_erase(hsh[x]); hsh[x] += del; hash_insert(hsh[x]); } void unite(int d, int x, int y) { x = find(d, x), y = find(d, y); if( x == y ) return ; if( rnk[d][x] < rnk[d][y] ) swap(x, y); dfs(d, y, pw[d]*x - pw[d]*y); fa[d][y] = x, sn[d][x].push_back(y); rnk[d][x] += rnk[d][y]; } int d, n, m; int read() { int x = 0; char ch = getchar(); while( ch > '9' || ch < '0' ) ch = getchar(); while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar(); return x; } int main() { d = read(), n = read(), m = read(); pw[1] = 1; for(int i=2;i<=d;i++) pw[i] = pw[i-1]*BASE; for(int i=1;i<=d;i++) for(int j=1;j<=n;j++) { fa[i][j] = j, rnk[i][j] = 1; hsh[j] += j*pw[i]; } for(int i=1;i<=n;i++) hash_insert(hsh[i]); for(int i=1;i<=m;i++) { int a = read(), b = read(), k = read(); unite(k, a, b), printf("%d\n", ans + n); } }
读入优化真好用.jpg。io
注意题目中说的是有序数对啊。