【UOJ】176 新年的繁荣【多路增广生成树】

题目链接: 新年的繁荣

题目大意:任意两点之间边的边权是两点点权的与,求边权和最大生成树
题目分析:每一次对一个连通块找一条出边(不连向自己),边权最大的同时所连的连通块编号最小,这些边去重后一定不会形成环,且每次至少减少一半的连通块,因此迭代复杂度为 logn

#include <bits/stdc++.h>
using namespace std ;

typedef pair < int , int > pii ;
typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 100005 ;

struct Seg {
    int u , v , c ;
    Seg () {}
    Seg ( int u , int v , int c ) : u ( u ) , v ( v ) , c ( c ) {}
} ;

Seg S[MAXN] ;
int top ;
int nxt[MAXN * 100][2] , minv[MAXN * 100] , maxv[MAXN * 100] , cur ;
int val[MAXN] ;
pii to[MAXN] ;
int p[MAXN] ;
int n , m ;

int F ( int x ) {
    return p[x] == x ? x : ( p[x] = F ( p[x] ) ) ;
}

int newnode () {
    nxt[cur][0] = nxt[cur][1] = 0 ;
    minv[cur] = MAXN ;
    maxv[cur] = 0 ;
    return cur ++ ;
}

void insert ( int v , int idx ) {
    int o = 0 ;
    for ( int i = m - 1 ; ~i ; -- i ) {
        int x = v >> i & 1 ;
        if ( !nxt[o][x] ) nxt[o][x] = newnode () ;
        o = nxt[o][x] ;
        if ( idx < minv[o] ) minv[o] = idx ;
        if ( idx > maxv[o] ) maxv[o] = idx ;
    }
}

int merge ( int x , int y ) {
    if ( !x && !y ) return 0 ;
    if ( !x ) return y ;
    if ( !y ) return x ;
    int o = cur ++ ;
    minv[o] = min ( minv[x] , minv[y] ) ;
    maxv[o] = max ( maxv[x] , maxv[y] ) ;
    nxt[o][0] = merge ( nxt[x][0] , nxt[y][0] ) ;
    nxt[o][1] = merge ( nxt[x][1] , nxt[y][1] ) ;
    return o ;
}

void dfs ( int o ) {
    if ( nxt[o][0] ) dfs ( nxt[o][0] ) ;
    if ( nxt[o][1] ) dfs ( nxt[o][1] ) ;
    nxt[o][0] = merge ( nxt[o][0] , nxt[o][1] ) ;
}

pii query ( int v , int idx ) {
    int res = 0 , o = 0 ;
    for ( int i = m - 1 ; ~i ; -- i ) {
        int x = v >> i & 1 ;
        int t = nxt[o][1] ;
        if ( x && t && ( minv[t] != idx || maxv[t] != idx ) ) {
            res |= 1 << i ;
            o = nxt[o][1] ;
        } else o = nxt[o][0] ;
    }
    return pii ( res , maxv[o] != idx ? maxv[o] : minv[o] ) ;
}

void solve () {
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%d" , &val[i] ) ;
        p[i] = i ;
    }
    int cnt = n - 1 ;
    LL ans = 0 ;
    while ( cnt ) {
        cur = 0 ;
        newnode () ;
        //printf ( "%d\n" , cnt ) ;
        for ( int i = 1 ; i <= n ; ++ i ) {
            if ( i == F ( i ) ) to[i] = pii ( -1 , 0 ) ;
            insert ( val[i] , p[i] ) ;
        }
        dfs ( 0 ) ;
        for ( int i = 1 ; i <= n ; ++ i ) {
            to[p[i]] = max ( to[p[i]] , query ( val[i] , p[i] ) ) ;
        }
        top = 0 ;
        for ( int i = 1 ; i <= n ; ++ i ) {
            if ( p[i] == i ) S[top ++] = Seg ( i , to[i].second , to[i].first ) ;
        }
        for ( int i = 0 ; i < top ; ++ i ) {
            int x = F ( S[i].u ) ;
            int y = F ( S[i].v ) ;
            if ( x != y ) {
                p[x] = y ;
                ans += S[i].c ;
                -- cnt ;
            }
        }
    }
    printf ( "%lld\n" , ans ) ;
}

int main () {
    while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;
    return 0 ;
}

压缩后代码:

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int>pii;
typedef long long LL;
#define clr(a,x)memset(a,x,sizeof a)
const int  MAXN=100005;
struct Seg{
    int u,v,c;
    Seg(){}
    Seg(int u,int v,int c):u(u),v(v),c(c){}
}S[MAXN];
int nxt[MAXN*100][2],minv[MAXN*100],maxv[MAXN*100],cur,val[MAXN],p[MAXN],n,m,top;
pii to[MAXN];
int F(int x){return p[x]==x?x:(p[x]=F(p[x]));}
int newnode(){
    nxt[cur][0]=nxt[cur][1]=0;
    minv[cur]=MAXN;
    maxv[cur]=0;
    return cur++;
}
void insert(int v,int idx){
    for(int i=m-1,o=0;~i;--i){
        int x=v>>i&1;
        if(!nxt[o][x])nxt[o][x]=newnode();
        o=nxt[o][x];
        if(idx<minv[o])minv[o]=idx;
        if(idx>maxv[o])maxv[o]=idx;
    }
}
int merge(int x,int y){
    if(!x||!y)return x?x:y;
    int o=cur++;
    minv[o]=min(minv[x],minv[y]);
    maxv[o]=max(maxv[x],maxv[y]);
    nxt[o][0]=merge(nxt[x][0],nxt[y][0]);
    nxt[o][1]=merge(nxt[x][1],nxt[y][1]);
    return o;
}
void dfs(int o){
    if(nxt[o][0])dfs(nxt[o][0]);
    if(nxt[o][1])dfs(nxt[o][1]);
    nxt[o][0]=merge(nxt[o][0],nxt[o][1]);
}
pii query(int v,int idx){
    int res=0,o=0;
    for(int i=m-1;~i;--i){
        int x=v>>i&1,t=nxt[o][1];
        if(x&&t&&(minv[t]!=idx||maxv[t]!=idx))res|=1<<i,o=nxt[o][1];
        else o=nxt[o][0];
    }
    return pii(res,maxv[o]!=idx?maxv[o]:minv[o]);
}
void solve(LL ans=0){
    for(int i=1;i<=n;p[i]=i,++i)scanf("%d",&val[i]);
    for(int cnt=n-1,i;cnt;){
        for(cur=0,newnode(),i=1;i<=n;insert(val[i],p[i]),++i)if(i==F(i))to[i]=pii(-1,0);
        for(dfs(0),i=1;i<=n;++i)to[p[i]]=max(to[p[i]],query(val[i],p[i]));
        for(top=0,i=1;i<=n;++i)if(p[i]==i)S[top++]=Seg(i,to[i].second,to[i].first);
        for(i=0;i<top;++i){
            int x=F(S[i].u),y=F(S[i].v);
            if(x!=y)p[x]=y,ans+=S[i].c,--cnt;
        }
    }
    printf("%lld\n",ans);
}
int main(){
    while(~scanf("%d%d",&n,&m))solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值