【每日一题】边的染色

戳我传送


思路:


1.链式向前星存图后,dfs跑一遍判读是否自身矛盾。
2.dfs再跑一遍,对每个联通块的元素个数sum-1求和k。
3.dfs再跑一遍,对每个涂了颜色的边组成的连通块的元素个数sum-1求和,再用k减去总和,ans=2^k。


原理


1.边的值可以看作两个端点的异或值。
2.对每个涂了颜色的边组成的连通块,假设一个点为1,其余的点的值也随之确定了。比如1-3边的值为1,3-2边的值为0,如果给1取1,那么可以得到3=0,2=0。(这个联通快如果不是环,就不会自身矛盾,如果是环才有可能自身矛盾)如果又涂回到了起点(即这个连通块是圆),如果颜色和原来的一致就不矛盾,如果此时应该涂的颜色和第一次假设的颜色矛盾,那么存在自身矛盾,直接输出0。
3.思路的2和3都是从一个点遍历完整个连通块计算有多少个元素就是连通块的大小了。(一个联通快内可以有许多个环,一开始以为就一个)
3.一条边可以取0、1,两个点可以取0、0,0、1,1、0,1、1。两条边有四种可能,三个端点有8种可能。所以自由边的方案数是自由端点的方案数的1/2倍,假设某连通块有k个点,那么边的方案数等于2^(k-1)。
图片说明
知道原理后,我们发现思路1的dfs跑图时,只要存那些染了色的边,没染色的边不需要存。思路2、3的dfs只是想要连通块的大小,这个并查集预处理一下就好了。复杂度图片说明 (n log n)

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template <class T>
inline void read(T &res) {
    char c; T flag = 1;
    while ((c = getchar()) < '0' || c > '9')
        if (c == '-')
            flag = -1;
    res = c - '0';
    while ((c = getchar()) >= '0' && c <= '9')
        res = res * 10 + c - '0';
    res *= flag;
}
int fa[100005],size[100005],col[100005],f[100005],size2[100005];
int head[100005],Next[100005<<1],to[100005<<1],w[100005<<1],tot;
void add(int x,int y,int c) {
    to[++tot]=y;
    Next[tot]=head[x];
    w[tot]=c;
    head[x]=tot;
}
int find1(int x) {
    return fa[x]==0?x:(fa[x]=find1(fa[x]));
}
int find2(int x) {
    return f[x]==0?x:(f[x]=find2(f[x]));
}
bool dfs(int x,int c) {
    col[x]=c;
    for(int i=head[x];i;i=Next[i]) {
        int y=to[i];
        if(col[y]!=-1&&col[x]^w[i]!=col[y]) return 0;
        if(col[y]==-1&&!dfs(y,col[x]^w[i])) return 0;
    }
    return 1;
}
ll qpow(ll a,ll b) {
    ll ans=1;
    while(b) {
        if(b&1) ans=ans*a%998244353;
        a=a*a%998244353;
        b>>=1;
    }
    return ans;
}
int main() {
    int n,m,sum=0;
    read(n),read(m);
    fill(size+1,size+1+n,1);
    fill(size2+1,size2+1+n,1);
    while(m--) {
        int x,y,z,i,j;
        read(x),read(y),read(z);
        i=find1(x),j=find1(y);
        if(i!=j) {
            fa[i]=j;
            size[j]+=size[i];
        }
        if(z!=-1) {
            add(x,y,z);
            add(y,x,z);
            i=find2(x),j=find2(y);
            if(i!=j) {
                f[i]=j;
                size2[j]+=size2[i];
            }
        }
    }
    fill(col+1,col+1+n,-1);
    for(int i=1;i<=n;++i) {
        if(col[i]==-1&&!dfs(i,1)) {
            puts("0");
            return 0;
        }
        if(!fa[i]) sum+=size[i]-1;
        if(!f[i])    sum-=size2[i]-1;
    }
    printf("%lld\n",qpow(2,sum));
}
每日一题 文章被收录于专栏

牛客每日一题

全部评论

相关推荐

工作基本定了,比较满意。这深度学习真是难搞啊,论文看不懂,代码看不懂,不会改,创新点???&nbsp;还有一个月中期,怎么搞,已经抑郁了,没有小论文要求,我真是非常幸运了,不然就我这科研能力…………&nbsp;感觉自己对自己失去信心,无时无刻不在否定自己取得一些成绩都是运气好罢了,真的能顺利毕业吗?严重内耗、无力感爆棚啊…………
AI牛可乐:哎呀,听起来你最近压力好大呀,毕业论文和工作的双重压力一定让你感觉像是背了两座山呢。深度学习确实挺复杂的,就像是要在一片迷雾中找到出路一样。但是,你已经很棒了,工作都定好了,这是很大的成就呢! 论文的事情,别太担心。每个人都有看不懂代码和论文的时候,这是正常的。至于创新点嘛,有时候灵感就像小猫一样,需要一点一点地哄出来的。你可以先试试把你看过的资料整理一下,说不定就能找到新的思路呢。 中期答辩前,你可以列个计划,一步一步来,把大问题分解成小问题,一点一点克服。记得,你不是一个人在战斗,你的导师、同学都是你的战友哦。 至于信心嘛,别否定自己,每个人都会有低谷的时候。你已经取得的成绩,都是你实力的体现。毕业不是问题,只要你不放弃,就一定能顺利走过去的。加油哦,我相信你可以的! 对了,如果你愿意的话,可以点击我的头像,我们私信聊,也许我能帮你出出主意,或者就是简单地聊聊天,分散一下你的注意力也好呀。🐮💪🌟
点赞 评论 收藏
分享
找不到工作死了算了:没事的,雨英,hr肯主动告知结果已经超越大部分hr了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务