cf888G 完全图上最小生成树

这题思想就是boruka

具体就是我们从最高位来看,我们可以按照二进制最高位是0还是1来把数分为两块,他们内部各自进行连边形成最小生成树,然后这两个部分再连边,因为这样只会有一条最高位是1的边,如果我们让多个01对连边,那么会形成多条最高位权值是1的边,这明显会使结果更差,然后次高位及以下也如此处理。。

在操作上就是我们先把每个点的权值插入到01字典树,从最高位开始分治,在0和1这两个左右子树,它们首先连边,然后再在左右子树找一个点对,他们异或值要最小,为了让异或值最小,我们在左右子树向下找的时候,每一位尽量找相同的点,相同的话这一位异或值就会为0

时间复杂度:我们遍历了一遍字典树,这个复杂度可以看成nlogn,在字典树的每个节点又logn找了一遍异或最小值点对,所以总复杂度可以看成n(logn)^2


#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string>
#include<string.h>
#include<map>
#define ll long long
#include <iostream>
#include <math.h>
using namespace std;
#define maxn 3300000+5
ll tre[maxn][3],pos=0,sum[maxn];
void add(ll x)
{
    ll c=0;
    for(ll i=30;i>=0;i--)
    {
        ll y=(x>>i)&1ll;
        if(!tre[c][y]) tre[c][y]=++pos;
        c=tre[c][y];
        sum[c]++;
    }
}
ll Find(ll x,ll y,ll step)
{
    ll ans=1e9;
    if(step==-1) return 0;
    if(tre[x][0])
    {
        if(tre[y][0])//也就是说,尽量保证左右两边数每位尽量一致,这样一致异或就是0
        {
            ll w=Find(tre[x][0],tre[y][0],step-1);
            ans=w;
        }
        else
        {
            ll w=Find(tre[x][0],tre[y][1],step-1);
            ans=w+(1ll<<step);
        }
    }
    if(tre[x][1])
    {
        if(tre[y][1])//也就是说,尽量保证左右两边数每位尽量一致,这样一致异或就是0
        {
            ll w=Find(tre[x][1],tre[y][1],step-1);
            ans=min(ans,w);
        }
        else
        {
            ll w=Find(tre[x][1],tre[y][0],step-1);
            ans=min(ans,w+(1ll<<step));
        }
    }
    //printf("tre[%lld][0]=%lld tre[%lld][1]=%lld tre[%lld][0]=%lld tre[%lld][1]=%lld step=%lld ans=%lld\n",x,tre[x][0],x,tre[x][1],y,tre[y][0],y,tre[y][1],step,ans);
    return ans;
}
ll dfs(ll c,ll step)
{
    ll ans=0;
    ll ls=sum[tre[c][0]],rs=sum[tre[c][1]];
        if(step==-1) return 0;
    if(tre[c][0])ans+=dfs(tre[c][0],step-1);
    if(tre[c][1])ans+=dfs(tre[c][1],step-1);
    if(min(ls,rs)>0)
    {
        ans+=Find(tre[c][0],tre[c][1],step-1)+(1ll<<step);//说明0和1都有,那它们0集合就要和1集合连边,这条边贡献1<<step
                                                       //然后再在左右子树找一个点对,它们异或和最小
    }//printf("step=%lld tre[%lld][0]=%lld tre[%lld][1]=%lld ls=%lld rs=%lld ans=%lld\n",step,c,tre[c][0],c,tre[c][1],ls,rs,ans);

    return ans;

}
void init()
{
    pos=0;
}
int main()
{
    ll n;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        ll x;
        scanf("%lld",&x);
        add(x);
    }
    ll ans=dfs(0,30);
    printf("%lld\n",ans);
}
/*
3
1 2 3
*/



全部评论

相关推荐

点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务