zoukankan      html  css  js  c++  java
  • P4494[HAOI2018]反色游戏【圆方树】

    正题

    题目链接:https://www.luogu.com.cn/problem/P4494


    题目大意

    给出\(n\)个点\(m\)条边的一张无向图,节点有\(0/1\),每条边可以选择是否取反两边的点。

    开始求将所有节点变为\(0\)的方案,然后对于每个点询问删去这个点之后的方案

    \(1\leq T\leq 5,1\leq n,m\leq 10^5\)


    解题思路

    图的比较麻烦,先考虑树上的,那么每条边取不取反取决于它连接的子节点的黑白,但是根节点却无法这么调整。所以如果黑色个数为奇数个那么方案为\(0\),否则方案为\(1\)

    然后考虑一张连通图,考虑对于图中的一个生成树来说,无论非生成树上的边是否取反,都可以用这棵生成树调整回来,也就是如果黑色为奇数个方案为\(0\),否则方案为\(2^{m-n+1}\)

    因为原图不一定连通,设连通块个数为\(k\),那么第一问答案就是\(2^{m-n+k}\)(每个连通块的黑色个数为奇数个)。

    然后第二问,其实就是去掉这条边之后会分割一个连通块以影响答案。

    建立广义圆方树,统计每个点删去后会多产生的连通块数量以及是否有分割出来的连通块的黑色个数为奇数。

    顺带一提的是需要特判如果有两个或者以上的连通块黑色为奇数个,那么全都无解,否则只有可能删除掉黑色奇数连通块里的点。

    时间复杂度\(O(Tn)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<stack>
    #include<vector>
    #define ll long long
    using namespace std;
    const ll N=2e5+10,P=1e9+7;
    ll Z,n,m,dfc,sum,cnt,st[N],deg[N];
    ll dfn[N],low[N],pw[N],siz[N];
    bool tag[N],nok[N],v[N];
    stack<ll> s;char t[N];
    vector<ll>G[N],T[N];
    void tarjan(ll x){
    	dfn[x]=low[x]=++dfc;sum+=(t[x]=='1');
    	s.push(x);st[++st[0]]=x;
    	for(ll i=0;i<G[x].size();i++){
    		ll y=G[x][i];
    		if(!dfn[y]){
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    			if(low[y]==dfn[x]){
    				ll k;++cnt;
    				do{
    					k=s.top();s.pop();deg[k]--;
    					T[cnt].push_back(k);
    					T[k].push_back(cnt);
    				}while(k!=y);
    				T[cnt].push_back(x);
    				T[x].push_back(cnt);
    				deg[x]--;
    			}
    		}
    		else low[x]=min(low[x],dfn[y]);
    	}
    	return;
    }
    void dfs(ll x){
    	v[x]=1;st[++st[0]]=x;
    	siz[x]=(x<=n)&(t[x]=='1');
    	for(ll i=0;i<T[x].size();i++){
    		ll y=T[x][i];
    		if(v[y])continue;
    		dfs(y);siz[x]+=siz[y];
    		if(siz[y]&1)nok[x]=1;
    	}
    	return;
    }
    signed main()
    {
    	scanf("%lld",&Z);pw[0]=1;
    	for(ll i=1;i<N;i++)pw[i]=pw[i-1]*2%P;
    	while(Z--){
    		dfc=0;
    		memset(deg,0,sizeof(deg));
    		memset(nok,0,sizeof(nok));
    		memset(tag,0,sizeof(tag));
    		memset(dfn,0,sizeof(dfn));
    		memset(v,0,sizeof(v));
    		while(!s.empty())s.pop();
    		scanf("%lld%lld",&n,&m);
    		for(ll i=1;i<=2*n;i++)
    			T[i].clear(),G[i].clear();
    		for(ll i=1;i<=m;i++){
    			ll x,y;scanf("%lld%lld",&x,&y);
    			G[x].push_back(y);deg[x]++;
    			G[y].push_back(x);deg[y]++;
    		}
    		scanf("%s",t+1);cnt=n;
    		ll one=0,k=0;
    		for(ll i=1;i<=n;i++){
    			if(dfn[i])continue;
    			st[0]=sum=0;
    			tarjan(i);k++;
    			if(sum&1){
    				for(ll j=1;j<=st[0];j++)
    					tag[st[j]]=1;
    				one++;
    			}
    		}
    		if(one>1){
    			for(ll i=0;i<=n;i++)printf("0 ");
    			putchar('\n');continue;
    		}
    		else if(one)printf("0 ");
    		else printf("%lld ",pw[m-n+k]);
    		for(ll i=1;i<=n;i++){
    			if(v[i])continue;
    			st[0]=0;dfs(i);
    			for(ll j=1;j<=st[0];j++)
    				if((siz[i]-siz[st[j]])&1)nok[st[j]]=1;
    		}
    		for(ll i=1;i<=n;i++)
    			if(nok[i]||(one&&!tag[i]))printf("0 ");
    			else printf("%lld ",pw[m-n+k-deg[i]]);
    		putchar('\n');
    	}
    	return 0;
    }
    
  • 相关阅读:
    sublime text3快速编辑选中多行
    sublime text3实现多行快速编辑Ctrl+E或者Tab
    WPF集合控件实现分隔符(ItemsControl Separator)
    WPF的ListView控件自定义布局用法实例
    ASP.NET MVC4+BootStrap 实战(一)
    wpf 获取datagrid 模板列中的控件
    WPF之DataGrid篇:DataGridComboBoxColumn
    WPF DataGrid 样式分享
    WPF之DataTemplateSelector技巧
    【WPF】ComboBox:根据绑定选取、设置固定集合中的值
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14601846.html
Copyright © 2011-2022 走看看