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;
    }
    

      

  • 相关阅读:
    table标签去除默认边框
    自定义矢量图
    ClickJacking(点击劫持)
    css 字体不撑开默认块级元素问题
    meate 标签使用介绍
    intellij IDEA15 设置背景颜色
    JS 浮点数运算丢失精度解决方案
    IDEA 滚动条跳动问题
    JS
    异步变同步
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8970674.html
Copyright © 2011-2022 走看看