建通道【思维异或lowbit最小生成树XOR_MST】【2020牛客寒假算法基础集训营2】

眉间皱痕 提交于 2020-02-07 04:30:19

也就是说,我们贪心的来想,肯定是先要让相同值的元素先建边,然后再是选择lowbit(x) == 1的,然后再是2的,再是4的以此类推,那么贪心的思维就出来了。

然后咋一看,是

的做法啊!怎么优化?

我们肯定是要0和1进行合并的,每一位的0、1进行合并,是为了贪心的考虑,所以说,我们从并查集的角度来看,不妨先是0中的第一个元素去合并完1中的所有元素,然后保留一个1中的元素,继续,把0后面的所有元素去看能不能和1中的这一个元素进行合并来是的复杂度变成

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, root[maxN];
int fid(int x) { return x == root[x] ? x : root[x] = fid(root[x]); }
deque<int> vt[30][2];
map<int, int> mp;
int main()
{
    scanf("%d", &N);
    for(int i=1; i<=N; i++) root[i] = i;
    int line = 0;
    ll ans = 0;
    for(int i=1, val, id, fu, fv; i<=N; i++)
    {
        scanf("%d", &val);
        if(mp[val])
        {
            fu = fid(mp[val]); fv = fid(i);
            if(fu ^ fv) { line++; root[fu] = fv; }
            continue;
        }
        mp[val] = i;
        for(int j=0; j<30; j++)
        {
            id = val & 1;
            vt[j][id].push_back(i);
            val >>= 1;
        }
    }
    for(int i=0, fu, fv; i<30; i++)
    {
        if(line == N - 1) break;
        for(auto it=vt[i][0].begin(); it != vt[i][0].end(); it++)
        {
            fu = fid(*it);
            while(!vt[i][1].empty() && (vt[i][1].size() ^ 1))
            {
                fv = fid(vt[i][1].front());
                vt[i][1].pop_front();
                if(fu ^ fv) { line ++; root[fu] = fv; ans += 1LL << (ll)i; }
            }
            if(!vt[i][1].empty())
            {
                fv = fid(vt[i][1].front());
                if(fu ^ fv) { line ++; root[fu] = fv; ans += 1LL << (ll)i; }
            }
            if(line == N - 1) break;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!