• 【题解】 [AHOI2013]连通图 hash+差分/线段树分治+并查集 Luogu5227


    Legend

    给定一个无向连通图和若干个小集合,每个小集合包含一些边,对于每个集合,你需要确定将集合中的边删掉后改图是否保持联通。集合间的询问相互独立

    定义一个图为联通的当且仅当对于任意的两个顶点,都存在一条路径连接它们。

    点数 (1 le n le 10^5),集合数量 (1 le k le 10^5),边数量 (1 le m le 2 imes 10^5),集合大小 (1 le c le 4)

    Editorial

    一眼线段树分治对吧,但是这么做是非常没有意思的。

    考虑直接 ( m{dfs}) 建立一棵生成树,剩下的都是返祖边,不妨看成一次覆盖操作。

    这棵树有以下性质:

    • 仅割掉返祖边图一定联通。
    • 一条树边割掉之后,必须要把所有覆盖了它的返祖边割掉才能使得图不联通。

    所以我们把每一条返祖边设定一个随机 hash 值,把每一条树边 hash 值设置成所有覆盖了它的返祖边的 hash 异或和。

    这样每次查询只需要看是否存在一个包含返祖边的子集 hash 值异或和为 (0)

    每次查询的错误概率为:( extrm{unsigned int}) 范围选出 (c) 个整数,存在一个子集异或为 (0) 的概率。这个东西我显然是不会算的。

    复杂度 (O(malpha(n)+2^ck))

    Code

    代码实现的时候我很懒,没有检查集合是否有返祖先边。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9')
    		x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    const int MX = 2e5 + 233;
    
    int head[MX] ,tot = 1;
    struct edge{
    	int node ,next ,w;
    }h[MX << 1];
    void addedge(int u ,int v ,int w ,int flg = 1){
    	h[++tot] = (edge){v ,head[u] ,w} ,head[u] = tot;
    	if(flg) addedge(v ,u ,w ,false);
    }
    
    struct E{
    	int u ,v ,w;
    }e[MX];
    
    int vis[MX] ,cnt ,dis[MX];
    void dfs(int x ,int f = 0){
    	vis[x] = ++cnt;
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		d = h[i].node;
    		if(d == f) continue;
    		if(vis[d]){
    			if(vis[d] < vis[x]){
    				dis[d] ^= h[i].w;
    				dis[x] ^= h[i].w;
    			}
    		}else{
    			dfs(d ,x);
    			dis[x] ^= dis[d];
    			e[i / 2].w = dis[d];
    		}
    	}
    }
    
    int main(){
    	int n = read() ,m = read();
    	for(int i = 1 ; i <= m ; ++i){
    		e[i].u = read() ,e[i].v = read();
    		e[i].w = 1u * i * i + 1u * rand();
    		addedge(e[i].u ,e[i].v ,e[i].w);
    	}
    	dfs(1);
    	int k = read();
    	while(k--){
    		int s = read();
    		int a[4];
    		for(int i = 0 ; i < s ; ++i) a[i] = read();
    		int ok = 0;
    		for(int i = 1 ; i < 1 << s && !ok ; ++i){
    			int v = 0;
    			for(int j = 0 ; j < s ; ++j){
    				if((i >> j) & 1) v ^= e[a[j]].w;
    			}
    			if(v == 0) ok = true;
    		}
    		printf("%sonnected
    " ,ok ? "Disc" : "C");
    	}
    	return 0;
    }
    
  • 相关阅读:
    动手动脑——异常处理
    JAVA异常处理总结
    打印沙漏
    暑假第八周进度报告
    暑假第七周进度报告
    暑假第六周进度报告
    暑假第五周进度报告
    暑假第四周进度报告
    暑假第三周进度报告
    暑假第二周进度报告
  • 原文地址:https://www.cnblogs.com/imakf/p/13688027.html
Copyright © 2020-2023  润新知