4415. 点的赋值(二分图)

这篇博客探讨了一个无向无权图的点权值赋值问题,要求每个点的权值只能是1、2或3,并且每条边的两个端点权值之和为奇数。通过二分图染色法,博主提出了一个解决方案,即对图进行黑白染色,计算不同合法染色方案的数量。对于每个连通块,计算合法的权值分配方式,然后将所有连通块的方案数相乘得到总方案数。文章给出了具体的代码实现,用于计算不同赋值方法的数量并输出对998244353取模后的结果。
摘要由CSDN通过智能技术生成

给定一个 n 个点 m 条边的无向无权图。

点的编号为 1∼n。

图中不含重边和自环。

现在,请你给图中的每个点进行赋值,要求:

  1. 每个点的权值只能是 1 或 2 或 3。
  2. 对于图中的每一条边,其两端点的权值之和都必须是奇数。

请问,共有多少种不同的赋值方法。

由于结果可能很大,你只需要输出对 998244353 取模后的结果。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含两个整数 n,m。

接下来 m 行,每行包含两个整数 u,v,表示点 u 和点 v 之间存在一条边。

输出格式

一个整数,表示不同赋值方法的数量对 998244353 取模后的结果。

数据范围

前三个测试点满足 1≤T≤6,∑i=1Tm≤50。
所有测试点满足 1≤T≤3×105,1≤n≤3×105,0≤m≤3×105,1≤u,v≤n,∑i=1Tn≤3×105,∑i=1Tm≤3×105。

输入样例:

2
2 1
1 2
4 6
1 2
1 3
1 4
2 3
2 4
3 4

输出样例:

4
0

分析:

首先可以知道显然合法方案是 : (奇数−偶数−奇数−偶数)

因此考虑二分图染色法进行黑白染色 , 记录 黑色的数量cnt1,白色的数量cnt2,对于奇数有两种选择 , 而奇数既可以放黑色也可以放白色,因此对于一个连通块内的答案就是2的cnt1次方+2的cnt2次方,需要注意的是一旦一个连通块不合法则整体为0,所有连通块方案数相乘为答案。

代码如下:

#include <bits/stdc++.h>

using namespace std;
const int mod=998244353;
const int N=3e5+10;
vector<int>adj[N];
long long ans;
int st[N];
int t,n,m,flag,cnt1,cnt2;

bool dfs(int u,int an)
{
    //cout<<"u: "<<u<<endl;
    st[u]=an;
    int w=3-an;
    if(an==1) cnt1++;
    else cnt2++;
    for(int i=0;i<adj[u].size();i++)
    {
        int v=adj[u][i];
        
        if(st[v])
        {
            if(st[v]!=w)return false;
        }
        else
        {
            if(!dfs(v,w))return false;
        }
    }
    return true;
}

long long func(int x)
{
    long long res=1,la=2;
    while(x)
    {
        if(x%2)res=(res*la)%mod;
        la=(la*la)%mod;
        x/=2;
    }
    return res;
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        while(m--)
        {
            int u,v;
            cin>>u>>v;
            adj[u].push_back(v);
            adj[v].push_back(u);
        }
        ans=1;
        flag=0;
        for(int i=1;i<=n;i++)
        {
            if(st[i])continue;
            cnt1=cnt2=0;
            if(!dfs(i,1))
            {
                flag=1;
                break;
            }
            //cout<<cnt1<<" "<<cnt2<<endl;
            ans=(ans*(func(cnt1)+func(cnt2)))%mod;
        }
        if(flag)ans=0;
        
        cout<<ans<<endl;
        ans=0;
        fill(st,st+n+1,0);
        for(int i=1;i<=n;i++)adj[i].clear();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值