zoukankan      html  css  js  c++  java
  • [HAOI2018] 反色游戏

        做法超好想,细节调一辈子。 估计这句话最适合这个题了hhhh。

        首先一个很显然的想法:把边设成未知数,对点列异或方程,最后的解的个数就是 2^自由元 。 

        不过如果某个联通分量里有奇数个黑点,那么问题无解。然后我来证明一下:

            把一个联通分量里所有点代表的方程都异或起来,因为这个联通分量里的边恰好在其两个端点的行是1,所以最后异或出来的方程前m列都是0,如果有奇数个黑点的话,最后一列就是1,那么无解(不能0=1吧hhh)。

         直接高斯消元是 O(N^3) 的,肯定要gg了啊 (不过竟然给了60分消元hhh,出题人太良心了)。

        不过一般这种题我们如果按照特定的顺序消(shou)元(wan)的话,会得到一些很良心的结论(具体参考 bzoj 文艺计算姬)。

        考虑 边1 的两个端点 u,v,因为最后只能有一行对应在这列是1,所以我们就把第v行变成 第u行 异或 第v行,可以发现得到的新行的意义是:u点和v点合并后的新点(因为边1被消去了,正好对应一个点没有自环)。

        当然,如果我们考虑边i的时候,发现它的两个端点已经在一个联通分量里了,那么这条边就是自由元,因为我们现在在高斯消元的矩阵里已经找不到第i列是1的行了。

        我们把上述做法扩展一下,就可以得到一个能够算出全局答案的做法:初始答案是1,并查集维护连通性,如果尝试合并失败,那么答案*2。

        但是这还不够棒,因为本题要求出 删去 每一个点之后的答案。

        不过上述做法还是有点多余,因为我们仔细想一下它的过程,就可以发现其实答案就是  2^(m-n + 联通分量个数)。

        所以,现在我们还剩下的最大的问题就是:如何维护,删去一个点之后,可能带来的某个联通分量的黑点变成奇数个(对应无解)   和  联通分量个数的变动 (对应图的连通性)  的影响???

         

        跑个tarjan求割点的算法就好啦,第一个问题有些复杂,我们先解决一下第二个问题。

         

        第二个问题无非可以分以下几种情况:

            1.删去的节点是一个孤立顶点: 这种情况下 会使图中联通分量个数-1.

            2.删去一个割点: 这种情况可以在tarjan的时候顺带统计,不过如果这个点是dfs树的根的话答案还要再-1(因为上面就没有新的联通分量了)

            3.删去一个非孤立顶点且非割点:没有影响hhhh。

        但是不要忘了,删一个点的时候不仅会对联通分量个数产生影响,还会对图的总边数-点数 产生影响。

        至于第一个问题,我们可以在tarjan的时候处理出:删去一个点之后,图中 有奇数个黑点的联通分量 的个数的 变化量  derta[i]。

        怎么处理呢?   

        首先如果这个点是割点的话:

            1.如果这个点在的联通分量本来就有 奇数个黑点 的话,那么先让 derta[i]-- ,因为删去这个点会破坏这个联通分量。

            2.再让derta[i]加上 删去这个点之后, 在dfs树上不能回到祖先的有奇数个黑点的联通分量个数 。

            3.如果 dfs树根所在的联通分量在删去这个点之后也有奇数个黑点,那么 derta[i]++。

        如果这个点i是非割点且非孤立顶点的话,那么derta[i] = color[i] == 1 ? (color[this tree] == 1 ? -1 : 1) : 0 .

        如果这个点是孤立顶点的话,那么derta[i] = color[i] == 1 ?  -1 : 0.

        (呼。。。。。终于写完了累死我了。。。。)

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=200005,ha=1000000007;
    inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
    int to[maxn*2],ne[maxn*2],hd[maxn],num,n,m,T,ans,Sub[maxn];
    int Xor[maxn],NewC[maxn],NewB[maxn],BC,ci[maxn];
    int dfn[maxn],low[maxn],dc,tp,st[maxn],D[maxn];
    bool iscut[maxn];
    char S[maxn];
    inline void addline(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num,D[x]++;}
    inline void init(){
    	memset(hd,0,sizeof(hd)),num=0;
    	memset(NewB,0,sizeof(NewB));
    	memset(NewC,0,sizeof(NewC));
    	memset(D,0,sizeof(D));
    	memset(iscut,0,sizeof(iscut));
    	memset(dfn,0,sizeof(dfn));
    	memset(Sub,0,sizeof(Sub));
    	dc=BC=0;
    }
    inline int read(){
    	int x=0; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar());
    	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    	return x;
    }
    void W(int x){ if(x>=10) W(x/10); putchar(x%10+'0');}
    
    void dfs(int x,int fa){
    	st[++tp]=x,dfn[x]=low[x]=++dc;
    	int cd=0; Sub[x]=Xor[x];
    	for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa)
    	    if(!dfn[to[i]]){
    	    	cd++,dfs(to[i],x);
    			Xor[x]^=Xor[to[i]];
    			low[x]=min(low[x],low[to[i]]);
    			
    	    	if(low[to[i]]>=dfn[x]){
    	    		Sub[x]^=Xor[to[i]];
    			    iscut[x]=1,NewC[x]++;
    			    if(Xor[to[i]]) NewB[x]++;
    			}
    		}
    		else low[x]=min(low[x],dfn[to[i]]);
    	
    	if(cd==1&&fa<0) iscut[x]=NewB[x]=NewC[x]=0;
    	else if(fa<0) NewC[x]--;
    }
    
    inline void solve(){
    	for(int i=1;i<=n;i++) if(S[i]=='1') Xor[i]=1; else Xor[i]=0;
    	ans=m-n;
    	for(int i=1;i<=n;i++) if(!dfn[i]){
    		ans++;
    		dfs(i,-1);
    		if(Xor[i]) BC++;
    		if(!hd[i]) NewC[i]=-1;
    		
    		for(;tp;tp--) if(iscut[st[tp]]){
    			if(Xor[i]^Sub[st[tp]]) NewB[st[tp]]++;
    			if(Xor[i]==1) NewB[st[tp]]--;
    		}
    		else NewB[st[tp]]=S[st[tp]]=='1'?(Xor[i]?-1:1):0;
    	}
    	
    	if(BC) W(0); else W(ci[ans]); putchar(' ');
    	for(int i=1;i<=n;i++){
    		BC+=NewB[i],ans+=NewC[i]-D[i]+1;
    		if(!BC) W(ci[ans]); else W(0); putchar(' ');
    		BC-=NewB[i],ans-=NewC[i]-D[i]+1;
    	}	
    	puts("");
    }
    
    int main(){
    	ci[0]=1; for(int i=1;i<=200000;i++) ci[i]=add(ci[i-1],ci[i-1]);
    	T=read();
    	while(T--){
    		int uu,vv;
    		init(),n=read(),m=read();
    		for(int i=1;i<=m;i++) uu=read(),vv=read(),addline(uu,vv),addline(vv,uu);
    		scanf("%s",S+1),solve();
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    HDU 4024 Dwarven Sniper’s hunting(数学公式 或者是二分)
    二分图最大匹配总结
    HDU 4022 Bombing (STL应用)
    HDU 1847 Good Luck in CET4 Everybody!(组合博弈)
    HDU 1556 Color the ball(树状数组)
    HDU 4023 Game(博弈)
    HDU 1406 完数(水题)
    HDU 4021 24 Puzzle
    Oracle 多表查询优化
    【编程之美】字符串移位包含的问题(续)
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8970674.html
Copyright © 2011-2022 走看看