边的染色题解

边的染色

https://ac.nowcoder.com/acm/problem/17067

一点也不夸张看来差不多三天 各个大佬题解都取看,最后找到一个看懂了。

https://blog.csdn.net/jaihk662/article/details/80955631
看的这个大佬的学会了。
题意:我也看了很久 ,而且自测很多ac代码自己推了样列才完全弄懂,首先就是如果是环 那么必须要满足条件,环里面的所有边权^=0;如果是链那么就不管,随便搞,
题解,那么首先我们可以这样想,每条边的权值只可以是0|1,如果是环,那么我们是不是可以这样想,任何一条边的权值,我把它用该条边的两个端点各自假设一个权值,然后边权=左端点^右端点,是不是这样一来,无论我在这个环里面取什么都可以满足^和为0;
举列子:三个点1-2-3-1,这样一个环,三条边,边权1-2:a1^a2,边权2-3:a2^a3,边权3-1:a3^a1,那么我们把三个边权异或是不是=0,所以我们首先边转换为点来思考,那么现在是不是有N个点,然后每个点可以选择0|1,那么是不是就有2^n种,但是我们的环上是不是有可能有边权和相等呢,答案是肯定的,为什么,如果所有的边权取反一下,是不是答案就和这个边权和一样啦,是不是有一边有边权我们就应该除2,然后最后我们是不是可以找到一个环,再取找到该环被标记的边,该环所有种类数/2^(已经标记的个数-1),因为已经标记的就不是2的标记点次方,而是1了,因为已知嘛,为什么要-1,是因为我们是以点来计数的。

#include <bits/stdc++.h>
#define fio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define debug(x) cout << #x << ": " << x << endl;
#define debug1(x) cout<<"xxx"<<endl;
#define ll long long
#define ull unsigned long long
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#define mse(a,b) memset(a,b,sizeof a);
using namespace std;
const int maxx=1e5+100;
const int mod=998244353;
struct node
{
    int x,val;
};
vector<node>ans[maxx];
 cnt,sum,ok,vis[maxx],scc[maxx];
ll all[maxx],er[maxx]= {1};
ll qpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=a*ans%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
void dfs(int u)
{
    sum++;//第二次dfs使用来找连通块的个数
    for(int i=0;i<ans[u].size();i++)
    {
        if(ans[u][i].val==-1)
            continue;
        int v=ans[u][i].x;
        int val=vis[u]^ans[u][i].val;
        if(vis[v]==-1)
        {
            vis[v]=val;
            dfs(v);
        }
        else if(vis[v]!=val)//每次判断是否构成的环能否满足题意
            ok=1;
    }
}
void dfs2(int u,int k)
{
    sum++;记录该父节点的所有儿子
    scc[u]=k;
    for(int i=0;i<ans[u].size();i++)
    {
        int v=ans[u][i].x;
        if(scc[v]==0)
            dfs2(v,k);
    }
}
int main()
{
    fio;
   /// cout<<(4>>1)<<endl;
    int x,y,val,n,m;
    cin>>n>>m;
    for(ll i=1; i<=m; i++)
    {
        cin>>x>>y>>val;
        ans[x].push_back(node{y,val});
        ans[y].push_back(node{x,val});
        ///cout<<ans[x][0].x<<" "<<ans[x][0].val<<endl;
    }

    for(int i=1; i<=n; i++)
        er[i]=er[i-1]*2%mod;//cout<<er[i]<<endl;;
        //memset(vis,-1,sizeof vis);
         mse(vis,-1);
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==-1)
        {
            vis[i]=0;///试试改为一
            dfs(i);
        }
    }
    if(ok)
        cout<<0<<endl;
    else
    {
    ll daan=1;
        for(int i=1;i<=n;i++)
        {
            if(scc[i]==0)
            {
                sum=0;
                dfs2(i,++cnt);//cnt代表如果此无向图非联通,有多个环,那么就是分成不同的连通块来计算,最后相乘
//该dfs2里面是找连通块个数,
                all[cnt]=er[sum-1];
            }
        }
        //memset(vis,-1,sizeof vis);
        mse(vis,-1);
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==-1)
            {
                sum=0;
                vis[i]=0;
                dfs(i);//本次就是找所有i的儿子,
                all[scc[i]]=all[scc[i]]*qpow(er[sum-1],mod-2)%mod;//逆元求法
        }
            }
                for(int i=1;i<=cnt;i++)
                    daan=daan*all[i]%mod;
                cout<<daan<<endl;
    }
    return 0;
}
全部评论

相关推荐

拉丁是我干掉的:把上海理工大学改成北京理工大学。成功率增加200%
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务