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


    心路历程

    50pts 黑周围选奇数条边,白周围选偶数条边,然后(nm^3)异或消元

    初始是白是什么意思啊?

    又或者删一个点有什么便捷算法?

    SOL

    貌似按顺序消元会得到一些良心结论

    一条边(u,v),我们把第v行变成第u行异或第v行就消掉了这条边(u,v合并后的星点)

    若一条边的两个端点已经在一个连通分量里(消元的矩阵已经无这列为1的行了)

    并查集维护,若联通则*2

    扩展一些可得(ans=2^{m-n+联通分量个数})(若黑点个数为奇数则无解)

    于是就维护联通分量个数和黑点奇偶即可,前者可以(tarjan)求割点,后者要难一些

    对于后者,我们只用算出奇数黑点连通块的变化量(cb)即可

    若非割点或为孤立点,根据这个点的黑白和所在连通块的黑色数量即可得知

    若为割点

    1. 若所在连通块本来就有奇数个黑点,(cb--)(方便后面计算)
    2. 算出这个点删除后剩余连通块的奇黑个数,儿子连通块可直接在(tarjan)里算,(dfs)完后再处理这个点到根的连通块(异或即可得知是否有黑点)

    时间复杂度(O(n))

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    	return f==1?x:-x;
    }
    const int N=1e5+4,mod=1e9+7;
    int n,m,tim,top,dh,ans;
    int dfn[N],low[N],a[N],b[N],st[N],cb[N],divi[N],cut[N],mi[N];
    char s[N];
    vector<int>e[N];
    void tarjan(int x,int fa){
    	dfn[x]=low[x]=++tim;
    	st[++top]=x;
    	int fl=0;
    	b[x]=a[x];
    	for(auto v:e[x]){
    		if(v==fa)continue;
    		if(!dfn[v]){
    			tarjan(v,x);
    			low[x]=min(low[x],low[v]);
    			a[x]^=a[v];
    			fl++;
    			if(low[v]>=dfn[x]){
    				cut[x]=1;
    				divi[x]++;
    				b[x]^=a[v];
    				if(a[v])cb[x]++;
    			}
    		}
    		else low[x]=min(low[x],dfn[v]);
    	}
    	if(fl<2&&!fa)cut[x]=divi[x]=cb[x]=0;
    	else if(!fa)divi[x]--;
    }
    inline void clear(){
    	tim=dh=0;
    	for(int i=1;i<N;i++)e[i].clear();
    	memset(cut,0,sizeof(cut));
    	memset(dfn,0,sizeof(dfn));
    	memset(divi,0,sizeof(divi));
    	memset(cb,0,sizeof(cb));
    }
    int main(){
    	int T=read();
    	mi[0]=1;
    	for(int i=1;i<N;i++)mi[i]=(mi[i-1]<<1)%mod;
    	while(T--){
    		clear();
    		n=read();m=read();
    		for(int i=1,u,v;i<=m;i++){
    			u=read();v=read();
    			e[u].push_back(v);e[v].push_back(u); 
    		}
    		scanf("%s",s+1);
    		for(int i=1;i<=n;i++)a[i]=(s[i]^48);
    		ans=m-n;
    		for(int i=1,x;i<=n;i++)if(!dfn[i]){
    			tarjan(i,0);
    			ans++;
    			if(b[i])dh++;
    			if(e[i].empty())divi[i]=-1;
    			while(top){
    				x=st[top--];
    				if(cut[x]){
    					if(a[i])cb[x]--;
    					if(a[i]^b[x])cb[x]++;//到根节点的连通块还未判 
    				}
    				else cb[x]=(s[x]=='1'?(a[i]?-1:1):0); 
    			}
    		}
    		if(dh)cout<<"0 ";		
    		else cout<<mi[ans]<<" ";
    		for(int i=1;i<=n;i++){
    			dh+=cb[i];ans+=divi[i]-e[i].size()+1;
    			if(dh)cout<<"0 ";
    			else cout<<mi[ans]<<" ";
    			dh-=cb[i];ans-=divi[i]-e[i].size()+1; 
    		}
    		puts("");
    	} 
    	return (0-0);
    }
    
  • 相关阅读:
    MySQL 之 Metadata Locking 研究
    Spring, MyBatis 多数据源的配置和管理
    ThreadLocal 源码剖析
    Java多线程中的死锁问题
    Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)
    PriorityQueue和Queue的一种变体的实现
    被我们忽略的HttpSession线程安全问题
    Java并发之原子变量和原子引用与volatile
    使用Java实现单线程模式
    这些年无处安放的博客
  • 原文地址:https://www.cnblogs.com/aurora2004/p/12608555.html
Copyright © 2011-2022 走看看