题意

n个点m条边的无向图,每条边有一个权值,-1,0,1,表示这条边的染色情况,-1表示没有染色,0,1表示染的颜色。
需要对没有染色的边进行染色(0和1两种),求满足所有环的边异或和为0的染色方案数。

题解

  1. 直接对边赋值很难搞,那么考虑对点赋值,再转换为边的赋值,边的值就是两个端点的异或值,这样的好处就在于,在环中的一个点的权值会被相邻边的异或两次,两个相同的数异或就为0,这样就可以保证环的边异或和为0.
  2. 考虑这样赋值的方案数,每个点可以有两种选择,在n个点连通的情况下,n个点就有2^n种方案,那么边的方案是多少呢,两个相邻点(x,y),[x权值为1,y权值为0]和[x权值为0,y权值为1]的情况下,边的权值是相同的,类似的,[x权值为1,y权值为1]和[x权值为0,y权值为0]也是相同的,由此可以得出,边的染色方案是2^n-1,也就是点染色方案数的1/2.
  3. 接下来考虑有的边已经赋值的情况,对于赋值了的边(x,y),我们对x点赋值,就能得出y的点值(x^y=(x,y)那么x^(x,y)=y)。也就是说,对于一些赋值了的边的连通块,只要确定了一个点的权值,其他所有的点就能确定了,假设这些通过赋值了的边连在一起的点的个数是k,那么总的方案数就要除以2^(k-1).
  4. 计算方案数之前要判断原图是否已经是不满足条件的了.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 7, mod = 998244353;
    typedef long long ll;
    int head[maxn], to[maxn<<1], nex[maxn<<1], edge[maxn<<1], tot;
    void init() {
     memset(head, -1, sizeof(head));
     tot = 0;
    }
    void add(int x, int y, int z) {
     to[tot] = y;
     edge[tot] = z;
     nex[tot] = head[x];
     head[x] = tot++;
    }
    int col[maxn], vis[maxn], sum;
    int dfs(int x, int c) {
     col[x] = c;
     for (int i = head[x]; ~i; i = nex[i]) {
         int v = to[i];
         if(edge[i] == -1) continue;//改边如果是没有限定权值的,直接跳过,这条边不会对点染色产生影响
         if(col[v] != -1 && (c^edge[i] != col[v])) return 0;//如果v点已经染色,那么通过边权值异或点权值来判断v点是否与染色矛盾
         if(col[v] == -1 && (dfs(v, c^edge[i]) == 0)) return 0;//如果v没有染色,dfs继续判断是否矛盾,通过边点权值异或传递权值
     }
     return 1;
    }
    void dfs1(int x, int fx) {
     if(vis[x]) return;
     vis[x] = 1;
     sum++;
     for (int i = head[x]; ~i; i = nex[i]) {
         int v = to[i];
         if(v == fx) continue;
         dfs1(v, x);
     }
    }
    void dfs2(int x, int fx) {
     if(vis[x]) return;
     vis[x] = 1;
     sum++;
     for (int i = head[x]; ~i; i = nex[i]) {
         int v = to[i];
         if(edge[i] == -1) continue;
         if(v == fx) continue;
         dfs2(v, x);
     }
    }
    int main()
    {
     int n, m; init();
     scanf("%d%d", &n, &m);
     for (int i = 1, u, v, w; i <= m; i++) {
         scanf("%d%d%d", &u, &v, &w);
         add(u, v, w); add(v, u, w);
     }
     /*判断原图是否已经不满足环的异或和为0了*/
     memset(col, -1, sizeof(col));
     for (int i = 1; i <= n; i++) {
         if(col[i] == -1) {//如果这个点没有染色
             if(dfs(i, 1) == 0) {//给这个点染成1, dfs判断是否矛盾
                 printf("0\n");
                 return 0;
             }
         }
     }
     /*计算连通块的边数和*/
     memset(vis, 0, sizeof(vis));
     int k = 0;
     for (int i = 1; i <= n; i++) {
         if(!vis[i]) {
             sum = 0;
             dfs1(i, 0);
             k += sum - 1;
         }
     }
     /*计算已经赋值的边连通块里的条数*/
     memset(vis, 0, sizeof(vis));
     for (int i = 1; i <= n; i++) {
         if(!vis[i]) {
             sum = 0;
             dfs2(i, 0);
             k -= (sum - 1);
         }
     }
     ll ans = 1;
     for (int i = 1; i <= k; i++)
         ans = ans * 2 % mod;
     printf("%lld\n", ans);
    }
    

```