[HAOI2018]反色游戏

这道题想必得给出题人寄刀片了

题意:有N个点,每个点有黑白两色,给你一个关于N个点M条边的图,每次你可以选一条边(u,v)然后将u,v两点反色,每条边最多选1次,问最终有多少方案使得每个点都是白色,显然最多有2^m的方案数

如果我们对于第i个点,删掉该点以及与其相连的边,新的答案是多少(对于每个点都要求)

解析:

我们考虑一下经典的问题:从x1,x2,x3....xn中选出一些数使得他的xor为s,那么如果他有方案,那么他的方案数就是2^(自由元的个数)(想必大家都会

然后我们思考一下这道题,发现直接用上述结论就直接挂了,那么由于显然涉及到图论,那么我们思考一下图论与上述问题的联系

这个问题就是把上述M条边中选出一些,使得他们的xor为初始的点的颜色状态

/****************************************************************************************************/

思考着,思考着,思考着,思考着.................................

我们灵光一现

我们在不断删掉那些可以被张成的向量的时候,在这个图论性质下就是如果加入的边(u,v),uv两点已经联通,那么就表明这条边是可以被张成的,直接选u,v的那条路径去张成就行了

/****************************************************************************************************/

那么我们已经完成了第一步(就是我们把向量张成与点连通性所关联)

那么如果没有删点操作我们就可以做了,然而.......

/****************************************************************************************************/

但是我们发现我们只会删掉一个点

so?

我们思考一下上述问题已经与连通性相关

那么删掉一个点?

所以就是割点与非割点的区别了

然后我们分类讨论一下

1.割点,太烦了(我要给出题人寄刀片×2

我们思考一下我们在tarjan的时候

一个割点的子树分为两种(我要给出题人寄刀片×3

第一种就是该子树内部有向该点祖先连的边

第二种就是该子树内部没有向该点祖先连的边

2.如果不是割点

怎么想?

自己思考,到这了,应该没问题了

细节?

不告诉你(自己思考)

我要给出题人寄刀片×4!!!!!!!!

然后我就WA飞了

心累

大家自己感受吧

/****************************************************************************************************/

想了想还是贴代码吧,由于博主最后已经神智不清,所以该程序也和我一样(我注释不想加了,我相信你们应该不会去想看我代码的)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int N=1e5+10;
struct node{
	int u,to;
};
node edge[N<<1];
int head[N+10],sum[N+10],low[N+10],dfn[N+10],col[N+10],s1[N+10],size[N+10],deg[N+10];
char s[N+10];
long long fac[N+10],ans[N+10],p[N+10],q[N+10],anscol[N+10];
int k,x,y,s_fin,sumnow,dfstime,n,m,T;
bool b[N+10],c[N+10];
void add(int x,int y){
	edge[k].u=y;
	edge[k].to=head[x];
	head[x]=k++;
}
long long solve(int t,int x,int y)
{
	if (y==0) return 1;
	else 
	if (t&1) return 0;
	else  return fac[x-y+1]; 
}
int tarjan(int now,int fa)
{
    dfn[now]=low[now]=++dfstime; size[now]=1; if (s[now]=='1') s1[now]=1; else s1[now]=0; col[now]=sumnow;
    // cout << now << endl;
    for (int i=head[now];i!=-1;i=edge[i].to)
    {
       int u=edge[i].u;
       deg[now]++;
       if (u==fa) continue;
       if (!dfn[u]) 
       {
       	size[now]+=tarjan(u,now);  sum[now]+=sum[u]+1; s1[now]+=s1[u]; low[now]=min(low[now],low[u]);
       	if (low[u]>=dfn[now]) b[now]=1;
       }
       else {
       	if (dfn[u]>dfn[now]) sum[now]++;
       	low[now]=min(low[now],dfn[u]);
       }
    }
    if (deg[now]==1) b[now]=0;
    return size[now];
}
void dfs(int now,int top)
{
	c[now]=1;
	if (!b[now]) ans[now]=solve(s1[top]-(s[now]=='1'),sum[top]-deg[now],size[top]-1);
	int la=deg[now],sz=1;
	int ss=(s[now]=='1');
	for (int i=head[now];i!=-1;i=edge[i].to)
	{
		int u=edge[i].u;
		if (c[u]) continue;
		dfs(u,top);
		if (b[now]&&low[u]>=dfn[now]) ans[now]=(ans[now]*solve(s1[u],sum[u],size[u])),la+=sum[u],sz+=size[u],ss+=s1[u];
	}
    if (b[now]) ans[now]=(ans[now]*solve(s1[top]-ss,sum[top]-la,size[top]-sz));
}
int main()
{
	scanf("%d",&T);
	fac[0]=1; 
	for (int i=1;i<=N;i++) fac[i]=(fac[i-1]*2)%mod;
	while (T--)
	{
		scanf("%d%d",&n,&m);
		k=0; dfstime=0; sumnow=0;
		memset(head,-1,sizeof(head));
		for (int i=1;i<=m;i++)
		{  
            scanf("%d%d",&x,&y);
            add(x,y); add(y,x);
		} 
		scanf("%s",s+1);
		memset(b,0,sizeof(b));
        memset(deg,0,sizeof(deg));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(size,0,sizeof(size));
        memset(sum,0,sizeof(sum));
        memset(ans,0,sizeof(ans));
        memset(c,0,sizeof(c));
        memset(deg,0,sizeof(deg));
        for (int i=1;i<=n;i++) ans[i]=1;
        long long ans_fin=1;
        for (int i=1;i<=n;i++) 
        	if (!dfn[i]) {
            ++sumnow;
        	tarjan(i,0);
        	dfs(i,i);
        	if (!(s1[i]&1)) ans_fin=(ans_fin*fac[sum[i]-size[i]+1])%mod,anscol[sumnow]=fac[sum[i]-size[i]+1]; else
        	ans_fin=0,anscol[sumnow]=0;
        }
        p[0]=p[sumnow+1]=1; q[0]=q[sumnow+1]=1;
        for (int i=1;i<=sumnow;i++) p[i]=(p[i-1]*anscol[i])%mod;
        for (int i=sumnow;i>=1;i--) q[i]=(q[i+1]*anscol[i])%mod;
        printf("%lld",ans_fin);
        for (int i=1;i<=n;i++) printf(" %lld",ans[i]*p[col[i]-1]%mod*q[col[i]+1]%mod); 
        printf("\n");
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值