题目:

n个点,m条边的无向图。有些边被标记为0或1,有些还未标记。问你将剩下的边用0或1标记,满足图中所有环边权异或和为0的方案数。mod998244353


做法:

这题不会,看题解才懂。。
边权的方案不好弄,考虑点权。这里的思想就是,假如我确定了某个环上所有点的点权,使点权异或和为0。那么边(u,v)的边权用val[u]^val[v]得到,是满足边权异或和为0的。也就是点权与边权可以建立映射关系。但是请注意不是一一映射的关系。比如,点权(1,1)和(0,0)同时映射边权0。所以点权的2种方案对应边权的1种方案。而这两种点权方案互反。
首先我们用已标记的边新建一个图。然后我们选一个未标记的点u给它标记上0。(可1可0,我们只处理0就行)。我们发现由于新图中边权都是确定的,所以一旦确定u的标记,和u联通的点的标记也都确定了。dfs更新一下。然后重复以上选点u,标记0的步骤。并且我们记录一下一共选了几个起点u标记了0,记为cnt。
这一步做了什么呢?
求出了新图的联通块数cnt。也判定了有无解。(若在标记过程中出现冲突,就是无解的情况,输出0)
接下来我们把目光放在未标记的边上。这些边是确定方案的关键!
现在我们可以将这些未标记的边视为使新图中2个不连通的块联通的边。我们设新图中方案数1,每2个新图中的联通块被这些边联通,总方案数*2。为什么会这样呢?因为使新图的联通块联通相当于两个联通块起点u的标记有更多的选择。想象一下,若这条边是0,是一种方案,是1也是一种方案。分别对应于两个联通块起点u的选取。
所以,搞了这么多。答案其实就是是新图和原图联通块数量差。当然,前提是没冲突。


代码:

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define debug(a) cout << #a ": " << a << endl
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
const int mod = 998244353;
int fa[N], col[N];
vector<pair<int,int> > g[N];
bool flag;
int getfa(int x){
    return fa[x] == x ? x : fa[x] = getfa(fa[x]);
}
void dfs(int u, int p){//染色过程
    if (!flag) return;
    for (int i = 0; i < g[u].size(); ++i){
        int v = g[u][i].first, w = g[u][i].second;
        if (col[v] == -1){
            col[v] = col[u]^w;
            dfs(v, u);
        }else if (col[u]^col[v] != w){
            flag = false; return;
        }
    }
}
int main(void){
    IOS;
    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; ++i) fa[i] = i;
    int block = n;
    for (int i = 1; i <= m; ++i){
        int u, v, w; cin >> u >> v >> w;
        if (getfa(u) != getfa(v)){
            block--, fa[getfa(u)] = getfa(v);//并查集,得到原图联通块数量
        }
        if (w != -1){
            g[u].push_back(make_pair(v, w));
            g[v].push_back(make_pair(u, w));//建新图
        }
    }
    memset(col, -1, sizeof col);
    int cnt = 0;
    flag = true;//flag代表有无冲突
    for (int i = 1; i <= n; ++i){
        if (col[i] == -1){
            col[i] = 0, dfs(i, i), cnt++;//选点,标记0,同时记录新图联通块数量cnt
        }
    }
    if (!flag){//有冲突
        cout << 0 << endl; return 0;
    }
    ll ans = 1;
    for (int i = 0; i < cnt-block; ++i) ans = ans*2%mod;//答案是2^(联通块数量差)
    cout << ans << endl; 
    return 0;
}