zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    给定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),依次描述每一个操作。

    Output
    输出m行m个正整数,依次表示每次操作之后满足条件的有序数对(a,b)的个数。

    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
    16

    @solution@

    考虑动态统计增量。
    当合并两个连通块时,我们采用启发式合并的方法,将小的一个个塞进大的里面。在一个个塞的时候,统计新多出来的在 d 张图都连通的点对数量。
    因为是启发式合并,每个点最多被暴力塞 log 次,所以这一部分的复杂度为 O(ndlogn)。

    怎么判断两个点在 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 序列与它相同。

    判定两个序列是否相等,除了逐个比较外还可以使用字符串哈希的方法。
    那么就可以通过将 find 序列给哈希了,得到的哈希值拿去找相同的值有多少个。
    这个可以再写一个哈希表搞定(注意哈希表 ≠ 字符串哈希,虽然都是哈希。。。)

    在合并连通块的时候,find 序列只会有一个位置发生改变,改哈希值可以 O(1) 改。

    @accepted code@

    #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
    ", ans + n);
    	}
    }
    

    @details@

    读入优化真好用.jpg。

    注意题目中说的是有序数对啊。

  • 相关阅读:
    tr 删除换行
    ImportExcelUtil 导入excel表格数据转换为对象存储
    苹果手机(ios)拍照上传图片旋转90度问题---java后台处理
    event兼容性解决
    event兼容性解决
    [CodeVS4919]线段树练习4
    [CodeVS4919]线段树练习4
    [CodeVS4919]线段树练习4
    Cocos2D-X2.2.3学习笔记12(瞬时动作)
    Cocos2D-X2.2.3学习笔记12(瞬时动作)
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11845722.html
Copyright © 2011-2022 走看看