• 【BZOJ5303】【HAOI2018】—反色游戏(结论+tarjan割点)


    传送门

    出题人的题解:

    有个显然的结论,只有一个联通块中有偶数个节点才有解
    为什么呢?我们将黑点任意两两配对,对一对黑点考虑它们之间的任意一条路径,将路径上的边的状态(操作or不操作)取反,立即可以得到一种合法方案,

    那显然在一棵树上的方案数只有1种
    考虑一个联通块的非树边,显然是可以替换掉树上的一条路径的
    那非树边的选或不选就对应了不同的点集,则方案数就是2mn+12^{m-n+1}
    而对于一个图,方案数就是2mn+p,p2^{m-n+p},p是联通块个数

    那对于删去一个点就很简单了,考虑一下删去对所在联通块和黑点数目的影响分类讨论就可以了

    注意特判一个点的情况

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    inline int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
    	return res*f;
    }
    const int N=100005;
    const int mod=1000000007;
    int n,m,adj[N],nxt[N<<1],to[N<<1],in[N<<1],pow2[N],cnt;
    int dfn[N],low[N],tot,cut[N],siz[N],vis[N],bel[N],belnum,sub[N];
    char s[N];
    inline void addedge(int u,int v){
    	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
    }
    inline void clear(){
    	memset(adj,0,sizeof(adj));
    	memset(dfn,0,sizeof(dfn));
    	memset(siz,0,sizeof(siz));
    	memset(sub,0,sizeof(sub));
    	memset(cut,0,sizeof(cut));
    	memset(in,0,sizeof(in));
    	cnt=tot=0;
    }
    void tarjan(int u,int fa){
    	low[u]=dfn[u]=++tot;
    	vis[u]=1,siz[u]=s[u]=='1';
    	bel[u]=belnum;
    	for(int e=adj[u];e;e=nxt[e]){
    		int v=to[e];
    		if(!dfn[v]){
    			tarjan(v,u);
    			siz[u]+=siz[v];
    			if(low[v]>=dfn[u]){
    				++cut[u],vis[u]&=siz[v]%2==0;
    				sub[u]+=siz[v];
    			}
    			else low[u]=min(low[u],low[v]);
    		}
    		else if(v!=fa){
    			low[u]=min(low[u],dfn[v]);
    		}
    	}
    	if(!fa)--cut[u];
    }
    int main(){
    	pow2[0]=1;
    	for(int i=1;i<N;++i)pow2[i]=pow2[i-1]*2%mod;
    	int T=read();
    	while(T--){
    		n=read(),m=read();
    		clear();
    		for(int i=1;i<=m;++i){
    			int u=read(),v=read();
    			addedge(u,v),addedge(v,u);
    			++in[u],++in[v];
    		}
    		scanf("%s",s+1);
    		int num=0,odd=0;
    		for(int i=1;i<=n;++i){
    			if(!dfn[i]){
    				belnum=i,tarjan(i,0);
    				++num,odd+=siz[i]&1;
    			}
    		}
    		int ans=m-n+num;
    		cout<<((odd)?(0):(pow2[ans]));
    		for(int i=1;i<=n;++i){
    			if(!in[i])cout<<" "<<((odd==siz[i])?(pow2[ans]):(0));
    			else{
    				if(vis[i]&&((siz[bel[i]]-(s[i]=='1')-sub[i])%2==0)&&(odd==(siz[bel[i]]&1)))
    					cout<<" "<<(pow2[ans-in[i]+1+cut[i]]);
    				else cout<<" "<<0;
    			}
    		}
    		puts("");
    	}
    }
    
  • 相关阅读:
    IDEA右键新建时没有Java Class选项
    捕获摄像头视频VC
    重叠IO与IOCP
    (八)内存管理与内存分配
    DebugView使用详解
    (六) 中断机制
    (五) proc文件系统
    bash 之备份文件
    bash 遍历目录文件
    (四) linux内核模块编程
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/11145639.html
Copyright © 2020-2023  润新知