边的染色
边的染色
https://ac.nowcoder.com/acm/problem/17067
题意:小团有一张n个点,m条边的无向图G,有些边上已经被标记了0或1,表示它的边权。
现在你需要给剩下的边标记边权为0或1,求有几种标记的方式满足:
对于G中任意一个环,里面所有边的边权的异或值为0。
环的定义如下:
对于任意k(k≥2)个点{a1,a2,...,ak},若对于所有的i<k满足ai与ai+1之间有边,且a1=ak成立,则这k个点构成一个环。
思路:
参考了该题解:https://blog.csdn.net/jaihk662/article/details/80955631
如该题解所说,首先换边权为点权,既边权为它连接的二个点权的异或值,这样对于一个环而言,所有边的异或值为0(相当于每个点权异或二次),在一个联通块中如果全为不确定的边,则该连通块的n个点一共有2的(n)次方的情况,但当一种点权值全取反时边权值全不变,所以情况为2的(n-1)次方,当有一条边确认时,边的一个端点确认时另一个端点也会确认,则会产生一个已确定的点,方案数则要除以二,所以一个有n个点(k个点为已确定的点)的连通块的方案数为2的(n-1)次方除以2的k次方,既2的(未确定的点个数-1)次方。用并查集统计联通块的情况。
注意:当一个环全为确定的边时,如果环边的异或值为1则方案数为0.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 998244353
int n, m, pre[100005], ran[100005], flag=1, f[100005], ma[100005], fa[100005];
//ma数组统计连通块点的个数, fa统计联通块已确定的点的个数。
struct w
{
int to, w;
}w;
vector<struct w> g[100005];
void inti()
{
for(int i=0; i<=n; i++)
{
pre[i]=i;
ran[i]=0;
}
}
int find(int x)
{
if(x==pre[x])
{
return x;
}
pre[x]=find(pre[x]);
return pre[x];
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
{
if(ran[x]<ran[y])
{
pre[x]=y;
}
else
{
pre[y]=x;
if(ran[x]==ran[y])
{
ran[x]++;
}
}
}
}
bool same(int x,int y)
{
return find(x)==find(y);
}
int dfs(int v,int pa,int k, int wa)
{
if(pa==-1)
{
f[v]=1;
}
else
{
f[v]=f[pa]^wa;
k++;
}
for(int i=0; i<g[v].size(); i++)
{
if(g[v][i].w!=-1)
{
if(pa!=g[v][i].to&&f[g[v][i].to]==-1)
{
k=k+dfs(g[v][i].to,v,0,g[v][i].w);
}
else if(f[g[v][i].to]!=-1)
{
if(f[g[v][i].to]^f[v]!=g[v][i].w)
{
flag=0;
}
}
}
}
return k;
}
ll fun(ll d,ll k)
{
ll s=1;
while(k)
{
if(k&1)
{
s=s*d%inf;
}
d=d*d%inf;
k=k/2;
}
return s;
}
int main()
{
scanf("%d %d",&n,&m);
inti();
memset(f,-1,sizeof(f));
for(int i=0; i<m; i++)
{
int u, v, t;
scanf("%d %d %d",&u,&v,&t);
w.to=v;
w.w=t;
g[u].push_back(w);
w.to=u;
g[v].push_back(w);
unite(u,v);
}
for(int i=1; i<=n; i++)
{
ma[find(i)]++;
}
ll ans=1;
for(int i=1; i<=n; i++)
{
if(f[i]==-1&&ma[find(i)]!=1)
{
int pan=dfs(i,-1,0,-1);
fa[find(i)]+=pan;
}
}
for(int i=1; i<=n; i++)
{
if(ma[find(i)]>1)
{
ans=ans*fun(2,ma[find(i)]-1-fa[find(i)])%inf;
ma[find(i)]=1;//防止同一个连通块重复计算
}
}
if(flag==0)
{
ans=0;
}
printf("%lld\n",ans);
return 0;
}

查看4道真题和解析