这道题想必得给出题人寄刀片了
题意:有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
#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");
}
}