[bzoj3569]DZY Loves Chinese II

若原图不连通,显然答案都是"Disconnected",不妨假设原图连通

任求一棵生成树,对每条非树边随机一个权值,并将树边的权值为所有"包含"其的非树边("包含"指树边在非树边两端点生成树的路径上),那么每一条边即都有一个边权

下面,只需要判定删除的边权是否存在非空子集异或和为0即可(存在即不连通)

具体实现上,随机值范围在long long中即可,求树边的权值可以差分,判定使用线性基即可

时间复杂度为$o(m+qklog V)$(其中$V$为随机值范围),可以通过

关于其的正确性,证明来自于这篇blog,摘抄如下——

必要性:

若删掉边后的图不连通,任选其中一个极大连通块$S$,考虑$S$和$complement_{V}S$(其中$V$为原图点集)之间的边,显然存在(指非空)且都被删除,下面只需要说明这些边异或和为0

关于这个,考虑所有非树边(包括不在$S$和$complement_{V}S$之间的边),对其分类讨论:

1.在$S$或$complement_{V}S$内部,将其两端点生成树的路径上$S$和$complement_{V}S$之间的边取出,由于每一条这样的边都会改变其所属的集合,而最终其所属集合没有变化,那么必然是偶数条,即其贡献为0

2.在$S$和$complement_{V}S$之间的边,同理取出这些边,必然是奇数条,即其贡献也为0

综上,即得证

充分性:

考虑一个异或和为0的非空边集,将其中的树边先删除,即将生成树划分为若干个连通块(不考虑非树边)

对这些连通块黑白染色,要求使得任意一条被删除的树边两端点所属连通块不同色

此时,考虑一条未被删除的非树边,在忽略随机的值很特殊的情况下,可以认为其两端点生成树的路径上在此边集中的边为偶数条,也即其所连结的两个连通块同色

由此,不难发现不同色的点对之间一定没有(未被删除的)边,也即这两个集合不连通,同时由于边集非空,即总存在树边,那么两个集合都非空

综上,即得证

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 #define M 500005
 5 #define L 63
 6 #define ll long long
 7 struct Edge{
 8     int nex,to;
 9 }edge[M<<1];
10 int E,n,m,q,x,y,ans,head[N],dfn[N];
11 ll z,a[L],val[M],sum[N];
12 void add(int x,int y){
13     edge[E].nex=head[x];
14     edge[E].to=y;
15     head[x]=E++;
16 }
17 void dfs1(int k,int fa){
18     dfn[k]=++dfn[0];
19     for(int i=head[k];i!=-1;i=edge[i].nex)
20         if (edge[i].to!=fa){
21             if (!dfn[edge[i].to])dfs1(edge[i].to,k);
22             else{
23                 if (dfn[k]<dfn[edge[i].to])continue;
24                 for(int j=0;j<L;j++)val[(i>>1)]=(val[(i>>1)]<<1)+rand()%2;
25                 sum[k]^=val[(i>>1)],sum[edge[i].to]^=val[(i>>1)];
26             }
27         }
28 }
29 void dfs2(int k,int fa){
30     dfn[k]=++dfn[0];
31     for(int i=head[k];i!=-1;i=edge[i].nex)
32         if (!dfn[edge[i].to]){
33             dfs2(edge[i].to,k);
34             val[(i>>1)]=sum[edge[i].to];
35             sum[k]^=sum[edge[i].to];
36         }
37 }
38 int main(){
39     scanf("%d%d",&n,&m);
40     memset(head,-1,sizeof(head));
41     for(int i=1;i<=m;i++){
42         scanf("%d%d",&x,&y);
43         add(x,y);
44         add(y,x);
45     }
46     dfs1(1,0);
47     scanf("%d",&q);
48     for(int i=1;i<=n;i++)
49         if (!dfn[i]){
50             while (q--)printf("Disconnected
");
51             return 0;
52         }
53     memset(dfn,0,sizeof(dfn));
54     dfs2(1,0);
55     while (q--){
56         scanf("%d",&x);
57         memset(a,0,sizeof(a));
58         int tot=0;
59         for(int i=1;i<=x;i++){
60             scanf("%d",&y);
61             z=val[(y^ans)-1];
62             for(int j=L-1;j>=0;j--)
63                 if (z&(1LL<<j)){
64                     if (!a[j]){
65                         a[j]=z;
66                         tot++;
67                     }
68                     z^=a[j];
69                 }
70         }
71         if (tot<x)printf("Disconnected
");
72         else{
73             printf("Connected
");
74             ans++;
75         }
76     }
77 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/15024702.html