题外话: 这题开始并没有什么思路,第二天参考并学习了雨神的思路和写法。感谢雨神大大和清楚姐^-^

          题意: 给出n个点和m条边的无向图,有一些边被‘0’或者‘1’染色了. 问你:给没有染色的边染色,使得一个环的异或值为0.求这样满足条件的环有多少种染色方案。

          首先,异或^ 。如果二进制同一位置上数相同,返回0,否则返回1. 如果2个相同的数异或一下,结果是0;不相同的数异或一下,结果是1.

图片说明

          如果说从边不好下手的话,我们可以先从点开始分析,首先如果n个点进行0/1染色的话,有2^n次种方案。

          这2^n次种方案都是异或和为0? 答案是YES

          因为把2个点权异或成边权,每个点会被异或2次,2次相当于抵消了,所以结果就是0。看下图:
图片说明

          那么把点的情况转化成边的情况的话,方案数会减少多少?

举一个例子:
图片说明

          总的方案数其实就是连通块中有多少个节点,然后对应的方案数就是2^(k-1),但是现在题目有一个条件,就是说,他可能会给出一些边权,我们上面讨论的是无任何边权的情况。 有边权的话,在边权连出来的连通块中,只有一个点能选择0或者1,只要那个点被确定了,剩下的点都能唯一确定,因为他有边权的限制。我们把这个连通块的大小为k的话,答案要/2^(k-1)
图片说明

          所以我们只要先求出所有的连通块的大小之和,然后减掉有边权的连通块的大小。
最后的结果就是2的个数,乘起来即可

          最后如果出现矛盾的情况,我们直接返回0.

矛盾样例如下:
4 4
1 2 1
2 3 1
3 4 1
4 1 0

结果 0

AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <deque>
#include <set>
#include <stack>
#include <cctype>
#include <cmath>
#include <cassert>

using namespace std;

typedef long long ll;

const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

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;
}

const int N=100000+100;

const int MOD=998244353;

int head[N],tot;//链式前向星的变量

int n,m;//n个顶点,m条边
int x,y,val;//读入m条边的信息

int color[N];//存每个点染的颜色

int vis[N];//这个点是否被标记

int sum;

struct Edge
{
    int e;
    int to;
    int val;
}edge[N<<1];

void add_edge(int u,int v,int val)
{//加边操作
    tot++;
    edge[tot].e=v;
    edge[tot].to=head[u];
    edge[tot].val=val;
    head[u]=tot;
}

int dfs(int u,int col)//u表示当前节点,col表示涂的颜色
{
    color[u]=col;
    for(int i=head[u];i!=0;i=edge[i].to)
    {
        int v=edge[i].e;//和当前u节点相连的下一个节点 v
        int w=edge[i].val;//和当前u节点相连的下一个节点v的边权
        if(w==-1)continue;//我们只看边权有的
        if(color[v]!=-1)
        {//如果节点v已经被染过色了,那么我们需要和u节点异或一下,看看结果和他两直接的边权吻合吗
            if(color[u]^color[v]!=w)
            {//如果不等于,就发发生矛盾了
                return 0;
            }
        }
        if(color[v]==-1)
        {//如果节点v没有被颜色,那么就递归染色下去

            int t=dfs(v,col^w);//下一个点的颜色是边权异或前一个颜色,这样能保证2个节点异或一下等于他的边权
            if(t==0)return 0;
        }
    }
    return 1;
}

void dfs1(int u)
{
    sum++;
    vis[u]=1;
    for(int i=head[u];i!=0;i=edge[i].to)
    {
        int v=edge[i].e;
        if(!vis[v])dfs1(v);
    }
}

void dfs2(int u)
{
    sum++;
    vis[u]=1;
    for(int i=head[u];i!=0;i=edge[i].to)
    {
        int v=edge[i].e;
        int w=edge[i].val;
        if(w==-1)continue;
        if(!vis[v])dfs2(v);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    //n个顶点,m条边
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&x,&y,&val);
        //val=-1表示没有标记,0或者1表示标记了

        //无向图正反存2次
        add_edge(x,y,val);
        add_edge(y,x,val);
    }

    memset(color,-1,sizeof(color));//-1表示这个点没有被标记颜色

    //首先我们要看已经被标记的边是不是存在前后矛盾的情况,如果存在直接输出0

    for(int i=1;i<=n;i++)
    {
        if(color[i]==-1)//如果当前的颜色没有被标记过
        {
            int f=dfs(i,1);//把这个点先涂上颜色为1
            if(f==0)
            {//出现矛盾
                printf("0\n");
                return 0;
            }
        }
    }


    //求连通块点的个数

    memset(vis,0,sizeof(vis));

    int ans=0;
    for(int i=1;i<=n;i++)
    {
        sum=0;
        if(!vis[i])
        {
            dfs1(i);
            ans+=(sum-1);
        }
    }

    memset(vis,0,sizeof(vis));

    for(int i=1;i<=n;i++)
    {
        sum=0;
        if(!vis[i])
        {
            dfs2(i);
            ans-=(sum-1);
        }
    }

    ll res=1;
    for(int i=0;i<ans;i++)
    {
        res=(res*2ll)%MOD;
    }

    printf("%lld\n",res);

    return 0;
}