Luogu4494 [HAOI2018]反色游戏 【割顶】
首先发现对于一个联通块有奇数个黑点,那么总体来说答案无解。这个很容易想,因为对每个边进行操作会同时改变两个点的颜色,异或值不变。
然后一个朴素的想法是写出异或方程进行高斯消元。
可以发现高斯消元的过程实际上就是合并两个点的过程,如果是一棵树的话那么答案一定是2。
对于树上每多的一条边,它在合并点的过程中会被消除掉,这意味着这个是一个自由元。所以我们发现连通图的答案是$2^{m-n+1}$。
这样第一问答案就可以求了。判完无解后答案为$2^{m-n+d}$,$d$为联通块数。
对于第二问,如果有超过两个联通块的黑点为奇数个,后面全部为0。
现在考虑删除一个点,没有改变连通性,那么只有两种情况,一种是其它联通块仍然有奇数个黑点,答案为0,否则看自己这个联通块整体的黑点个数异或上当前删除的点的颜色,为奇数则答案为0,否则答案不难想。
如果删除了一个点,改变了连通性,那么分别检查每个联通块的奇偶性,这个不难,答案也不难想。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 105000; 5 const int mod = 1e9+7; 6 7 int n,m,cnt; 8 vector<int> g[maxn]; 9 int a[maxn],flag; 10 11 int low[maxn],dfn[maxn],sz[maxn],cl,pa[maxn]; 12 int ans[maxn],pw2[maxn*2]; 13 14 void init(){ 15 memset(low,0,sizeof(low)); 16 memset(dfn,0,sizeof(dfn)); 17 memset(sz,0,sizeof(sz)); 18 memset(pa,0,sizeof(pa)); 19 memset(ans,0,sizeof(ans)); 20 memset(pw2,0,sizeof(pw2)); 21 memset(a,0,sizeof(a)); 22 for(int i=1;i<=n;i++) g[i].clear(); 23 n = m = cnt = flag = 0; 24 } 25 26 void dfs(int now,int fa){ 27 low[now] = dfn[now] = ++cl; 28 pa[now] = fa; sz[now] = a[now]; 29 for(int i=0;i<g[now].size();i++){ 30 int z = g[now][i]; 31 if(z == fa || dfn[z] > dfn[now]) continue; 32 if(dfn[z] == 0){ 33 dfs(z,now); sz[now]^=sz[z]; 34 low[now] = min(low[now],low[z]); 35 }else low[now] = min(low[now],dfn[z]); 36 37 } 38 } 39 40 void dfs2(int now,int fa,int dr){ 41 for(int i=0;i<g[now].size();i++){ 42 if(pa[g[now][i]] != now) continue; 43 dfs2(g[now][i],now,dr); 44 } 45 int isfuck = 0; 46 if(flag - dr) {ans[now] = 0;return;} 47 if(fa == 0){ 48 int mlgb = 0; 49 for(int i=0;i<g[now].size();i++){ 50 if(pa[g[now][i]] != now) continue; 51 mlgb++; isfuck += sz[g[now][i]]; 52 } 53 if(mlgb == 0){ans[now] = ans[0];} 54 else if(mlgb == 1){ 55 if(isfuck) ans[now] = 0; 56 else ans[now] = pw2[m-g[now].size()-(n-1)+cnt]; 57 }else{ 58 if(isfuck) ans[now] = 0; 59 else ans[now] = pw2[m-g[now].size()-(n-1)+(cnt-1+mlgb)]; 60 } 61 }else{ 62 int mlgb = 0; 63 for(int i=0;i<g[now].size();i++){ 64 if(pa[g[now][i]] != now) continue; 65 if(low[g[now][i]] >= dfn[now]){ 66 mlgb++; 67 isfuck += sz[g[now][i]]; 68 } 69 } 70 if(mlgb == 0){ 71 if(dr ^ a[now]) ans[now] = 0; 72 else ans[now] = pw2[m-g[now].size()-(n-1)+cnt]; 73 }else{ 74 if(isfuck) ans[now] = 0; 75 else if(dr^(isfuck&1)^a[now]) ans[now] = 0; 76 else ans[now] = pw2[m-g[now].size()-(n-1)+(cnt+mlgb)]; 77 } 78 } 79 } 80 81 void read(){ 82 scanf("%d%d",&n,&m); 83 for(int i=1;i<=m;i++){ 84 int u,v; scanf("%d%d",&u,&v); 85 g[u].push_back(v); g[v].push_back(u); 86 } 87 for(int i=1;i<=n;i++) scanf("%1d",&a[i]); 88 } 89 90 void work(){ 91 pw2[0] = 1; 92 for(int i=1;i<=m*2;i++) pw2[i] = pw2[i-1]*2%mod; 93 for(int i=1;i<=n;i++) { 94 if(!dfn[i]) { 95 dfs(i,0); cnt++; 96 if(sz[i]) flag++; 97 } 98 } 99 if(flag == 0){ans[0] = pw2[m-n+cnt];} 100 if(flag >= 2){ 101 for(int i=0;i<=n;i++) printf("0 "); 102 puts(""); 103 return; 104 } 105 for(int i=1;i<=n;i++) { 106 if(pa[i] == 0){dfs2(i,0,sz[i]);} 107 } 108 for(int i=0;i<=n;i++) printf("%d ",ans[i]); 109 puts(""); 110 } 111 112 int main(){ 113 int T; scanf("%d",&T); 114 while(T--){ 115 init(); 116 read(); 117 work(); 118 } 119 return 0; 120 }