

思路:
给每个点赋值(1,2,3)种的任意一个数,并保证相邻两个节点之和为奇数
由此可以推出:对每一个连通块中的任意一个奇数结点,都有两种赋值可能(1或3),统计每个连通块中的奇数(cnt1)和偶数(cnt2)顶点的个数,只考虑奇数结点的时候有2cnt12^{cnt1}2cnt1种,奇数偶数结点互换后有2cnt22^{cnt2}2cnt2,那么总的方案数就有2cnt1+2cnt22^{cnt1}+2^{cnt2}2cnt1+2cnt2种。
注意:
图不连通的时候,总方案数应为所有连通块的方案数累乘
当某个连通块只有一个结点的时候,该连通块方案数有3种
奇环无法染色,只要出现有奇数顶点的环,总方案数为0
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn= 3e5+10,mod=998244353;
ll t,n,m,u,v,ans,cnt1,cnt2,k,tot;
ll pre[maxn*2],head[maxn*2],vis[maxn*2];
bool flag;
struct node { //邻接表
int to,next;
} e[maxn*2];
ll qpow(ll a,ll b) { //快速幂模板
ll res = 1,base = a;
while(b) {
if(b&1) res = res*base%mod;
base = base*base%mod;
b >>= 1;
}
return res;
}
int xfind(int x) { //并查集模板
return (pre[x]==x ? x : pre[x]=xfind(pre[x]));
}
void add(int u,int v) {
e[tot].to=v;
e[tot].next=head[u];
head[u]=tot++;
}
void dfs(int u, int f, int status) {
k++;
vis[u] = 1;
if(status == 1) cnt1++;
else cnt2++;
for (int i = head[u]; i != 0; i = e[i].next)//对邻接表的dfs
if(e[i].to != f && !vis[e[i].to])
dfs(e[i].to, u, status^1);//status用来记录当前节点u为奇数还是偶数
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&m);
tot=1,flag=true;
for(int i=0; i<=n*2; i++) pre[i]=i,vis[i]=head[i]=0;
for(int i=1; i<=m; i++) {
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
if(xfind(u)==xfind(v)) flag=false;//很巧妙,但没看太懂
pre[xfind(u)] = xfind(v+n);
pre[xfind(v)] = xfind(u+n);
}
if(!flag) cout<<"0"<<endl;
else {
ans=1;
for(int i=1; i<=n; i++) {
if(vis[i]) continue;
k=cnt1=cnt2=0;
dfs(i,0,0);
if(k==1) ans=(ans*3)%mod;
else ans *= (qpow(2,cnt1)+qpow(2,cnt2)%mod);
}
printf("%lld\n",ans%mod);
}
}
}
本文深入探讨了图论中的一种特殊染色问题,通过给图的每个节点分配1、2或3三种颜色之一,保证相邻节点颜色之和为奇数。文章详细介绍了如何使用并查集和深度优先搜索(DFS)算法来解决这一问题,提供了AC代码实现,并讨论了图不连通、存在奇数顶点环等特殊情况下的解决方案。
472

被折叠的 条评论
为什么被折叠?



