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 }

 

posted @ 2019-03-11 17:00  menhera  阅读(187)  评论(0编辑  收藏  举报