B - Bitwise Exclusive-OR Sequence

传送门

题意:
n个数,m个限制条件,每个限制条件给出三个数x, y, w,前两个代表下标,\(a_x ^ a_y = w\), 然后满足这m个条件的n个数的最小的和是多少

思路:
就先用并查集对这些数进行初步的分类,在同一个并查集中的数字才有关联,而且违法情况也只可能出现在同一个并查集中的数字出现矛盾的情况,然后对每个数字看成二进制位,反正按照题目意思先按照一定的规则去,先满足m个限制条件,在考虑n个数和最小的情况,在满足m个条件,就是每回对于并查集的头先令他为0,然后根据这个数就可以推出其他的所有的数,然后在推出其他所有的数的过程中,如果数和数之间存在矛盾,那就是-1,如果没有矛盾,先把这个数二进制为记录下来,那这样一个连通块下来,就是对于每一个二进制位1,0的相斥个数就确定了,然后贪心的让1的个数小,最后就是答案

总结:
这个的其实可以抽象为对并查集里的数就行染色的操作,就是对数进行标记,然后还有就是这题的解题是一步一步进行解题的,满足条件是一步一步来满足的,总的收获就是并查集染色,判断合法非法,并查集合图结合起来

点击查看代码
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);
#define endl '\n'
using namespace std;

typedef long long ll;
const int MAXN = 1e5 + 10;
int n, m;
vector<pair<int, int>> edge[MAXN];
int fa[MAXN], num[MAXN], val[MAXN];

void init()
{
    for (int i = 1; i <= n; ++i)
        fa[i] = i, num[i] = 1, val[i] = -1;
}

int find(int x)
{
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

int main()
{
    IOS; cin.tie(0), cout.tie(0);
    cin >> n >> m;
    init();
    for (int i = 1, x, y, w; i <= m; ++i)
    {
        cin >> x >> y >> w, edge[x].push_back({y, w}), edge[y].push_back({x, w});
        int fax = find(x), fay = find(y);
        if (fax != fay)
            fa[fax] = fay, num[fay] += num[fax];
    }
    ll ans = 0;
    bool flag = true;
    for (int i = 1; i <= n; ++i)
    {
        if (i != find(i))
            continue;
        int cnt[31] = {0};
        val[i] = 0, flag = true;; 
        queue<int> q;
        q.push(i);
        while (!q.empty())
        {
            int x = q.front();
            q.pop();
            for (int j = 0; j < edge[x].size(); ++j)
            {
                int y = edge[i][j].first, w = edge[i][j].second;
                if (val[y] != -1)   //说明在之前已经对这个值有一个确定值了,只要是按照一定的顺序,如果合法,定然是可以判断的
                {
                    if (val[y] != (val[x] ^ w))
                    {
                        flag = false;
                        break;
                    }
                }
                else
                {
                    val[y] = val[x] ^ w;
                    for (int k = 30; k >= 0; --k)
                        cnt[k] += (1 & (val[y] >> k) ? 1 : 0);
                    q.push(y);    
                }
            }    
        }
        if (!flag)
            break;
        for (int k = 0; k <= 30; ++k)
            ans += (1 << k) * min(cnt[k], num[i] - cnt[k]);
    }
    cout << (flag ? ans : -1) << endl;
    return 0;
}

posted @ 2022-11-08 23:25  YUGUOTIANQING  阅读(80)  评论(0)    收藏  举报