链接:https://www.nowcoder.com/acm/contest/152/C
来源:牛客网
题目描述
小团有一张n个点,m条边的无向图G,有些边上已经被标记了0或1,表示它的边权。
现在你需要给剩下的边标记边权为0或1,求有几种标记的方式满足:
现在你需要给剩下的边标记边权为0或1,求有几种标记的方式满足:
对于G中任意一个环,里面所有边的边权的异或值为0。
环的定义如下:
对于任意k(k≥2)个点{a
1,a
2,...,a
k},若对于所有的i<k满足a
i与a
i+1之间有边,且a
1=a
k成立,则这k个点构成一个环。
输入描述:
第一行两个整数n, m。 接下来m行,每行3个整数u, v, w,表示有一条边(u,v),若w=-1则这条边上没有标记,否则w=0或1表示这条边上标记了w。 数据保证没有重边和自环。 1≤n≤100,000 0≤m≤min(n*(n-1)/2, 100000)
输出描述:
输出方案数对998,244,353取模的值。
先考虑一个简单的题:
题目:一个n个点m条边的无向联通图,要求你给每条边指定一个值,只能是0或1,使得最后所有的环边权异或和为0,求出任意一种方案即可
思路:先给每个点指定一个值0或1,随意指定,然后计算每条边的权值就为两个端点权值的异或,这样就一定满足要求,因为对于任意一个环,每个点权值刚好计算2次
再考虑一个稍微难点的题:
题目:一个n个点m条边的无向图,要求你给每条边指定一个值,只能是0或1,使得最后所有的环边权异或和为0,问有多少种不同的方案:
思路:这个图未必连通,先假设它是连通的,考虑上一道题的做法:给每个点随意指定0和1,然后通过点权计算边权最后都一定合法,而每个点都有赋0赋1两种不同方案,所以总共用2^n种不同方案,不过当中会不会有边权完全一样但点值不同情况呢?有,不过当前仅当所有点的值全部取反时才会一样,所以最后还要除以2,这道题的答案就是2^(n-1)
那么不连通这么办?只要对于每个联通图求出方案数然后乘起来即可
然后就是这道题
题目:这道题相对上道题只多了一个限制:有些边的权值已经给定了,你不能修改,求方案数
思路:还是考虑对于每个联通图求出方案数然后乘起来即可,其实上道题会做这道题就很简单了,先用二分图染色的方法判断是否本身就不合法,如果本身就不合法直接输出0,然后只要把那些有限制的边构成的所有联通块全部揪出来,这些联通块很显然1个端点的值确定了,那么整个联通块所有端点的值就全部确定了,直接答案除掉2^(p[i]-1)即可,其中p[i]是第i个联通块点的个数
#include<stdio.h>
#include<vector>
#include<string.h>
using namespace std;
#define LL long long
#define mod 998244353
typedef struct
{
int x, val;
}Res;
Res now;
vector<Res> G[100005];
int cnt, sum, ok, vis[100005], scc[100005];
LL all[100005], er[100005] = {1};
LL Pow(LL x, LL y)
{
LL ans = 1;
while(y)
{
if(y%2)
ans = ans*x%mod;
x = x*x%mod;
y /= 2;
}
return ans;
}
void Sech(int u)
{
int i, v, val;
sum++;
for(i=0;i<G[u].size();i++)
{
if(G[u][i].val==-1)
continue;
v = G[u][i].x;
val = vis[u]^G[u][i].val;
if(vis[v]==-1)
{
vis[v] = val;
Sech(v);
}
else if(vis[v]!=val)
ok = 1;
}
}
void Sech2(int u, int k)
{
int i, v;
sum++;
scc[u] = k;
for(i=0;i<G[u].size();i++)
{
v = G[u][i].x;
if(scc[v]==0)
Sech2(v, k);
}
}
int main(void)
{
LL ans;
int x, y, val, n, m, i;
scanf("%d%d", &n, &m);
for(i=1;i<=m;i++)
{
scanf("%d%d%d", &x, &y, &val);
now.x = y, now.val = val;
G[x].push_back(now);
now.x = x;
G[y].push_back(now);
}
for(i=1;i<=n;i++)
er[i] = er[i-1]*2%mod;
memset(vis, -1, sizeof(vis));
for(i=1;i<=n;i++)
{
if(vis[i]==-1)
{
vis[i] = 0;
Sech(i);
}
}
if(ok)
printf("0\n");
else
{
ans = 1;
for(i=1;i<=n;i++)
{
if(scc[i]==0)
{
sum = 0;
Sech2(i, ++cnt);
all[cnt] = er[sum-1];
}
}
memset(vis, -1, sizeof(vis));
for(i=1;i<=n;i++)
{
if(vis[i]==-1)
{
sum = 0;
vis[i] = 0;
Sech(i);
all[scc[i]] = all[scc[i]]*Pow(er[sum-1], mod-2)%mod;
}
}
for(i=1;i<=cnt;i++)
ans = ans*all[i]%mod;
printf("%lld\n", ans);
}
return 0;
}
/*
3 3
1 2 1
2 3 1
3 1 1
*/