CodeForces

题意:给定无向图,让你给点加权(1,2,3),使得每条边是两端点点权和维奇数。

思路:一个连通块是个二分图,判定二分图可以dfs,并查集,2-sat染色。 这里用的并查集(还可以带权并查集优化一下,或者干脆用dfs)。

计数的时候每个连通块单独考虑,我们从连通块的第一个点开始dfs,如果是该填奇数点,那么当前方案数*=2;分第一个点奇偶两种情况即可。

(多组输入一定注意初始化,这次CF多组输入好坑啊。。。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
const int Mod=998244353;
int Laxt[maxn],Next[maxn],To[maxn],cnt,num1,num2,vis[maxn],tot,ans;
void add(int u,int v){
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;
}
void dfs(int u,int f,int x,int y)
{
    tot++; vis[u]=1;
    if(x==1) num1=num1*2%Mod;
    if(y==1) num2=num2*2%Mod;
    for(int i=Laxt[u];i;i=Next[i]){
        if(To[i]!=f&&!vis[To[i]]) dfs(To[i],u,1-x,1-y);
    }

}
int fa[maxn];
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
int main()
{
    int T,N,M,u,v;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&N,&M);
        rep(i,1,N) Laxt[i]=0; cnt=0;
        rep(i,1,N+N) fa[i]=i,vis[i]=0;
        bool F=true;
        rep(i,1,M){
           scanf("%d%d",&u,&v);
           add(u,v); add(v,u);
           if(find(u)==find(v)) F=false;
           fa[find(u)]=find(v+N);
           fa[find(v)]=find(u+N);
        }
        if(!F) puts("0");
        else {
            ans=1;
            rep(i,1,N) {
                if(!vis[i]){
                    num1=1; num2=1; tot=0;
                    dfs(i,0,0,1);
                    if(tot==1) ans=3LL*ans%Mod;
                    else ans=1LL*ans*(num1+num2)%Mod;
                }
            }
            printf("%d
",ans);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/hua-dong/p/10126509.html