[AND最大生成树 分治 Trie || Kruskal] UOJ Goodbye Yiwei C #176. 新年的繁荣

30 篇文章 0 订阅
16 篇文章 0 订阅

这个直接上题解吧 :http://vfleaking.blog.uoj.ac/blog/1244


Kruskal 

用尛焱轟的话来说就是

枚举边权 可以发现一种边权最多只有O(m)个联通块需要合并 O(m 2^m a(n))


#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

inline char nc(){    
  static char buf[100000],*p1=buf,*p2=buf;    
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }    
  return *p1++;    
}    
    
inline bool read(int &x){    
  char c=nc(),b=1;    
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; else if (c==EOF) return 0;   
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;  return 1;  
}  

int n,m;
int a[1<<19],fat[1<<19];
ll ans;

inline int Fat(int u){
  return u==fat[u]?u:fat[u]=Fat(fat[u]);
}
inline bool Merge(int x,int y){
  x=Fat(x); y=Fat(y); if (x==y) return 0; fat[x]=y; return 1;
}

int main(){
  int x;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=n;i++){
    read(x); if (a[x]) ans+=x; else a[x]=x;
  }
  for (int i=0;i<(1<<m);i++) fat[i]=i;
  for (int i=(1<<m)-1;i;i--){
    for (int j=0;j<m && !a[i];j++) a[i]=a[i|(1<<j)];
    for (int j=0;j<m;j++)
      if (a[i|(1<<j)] && Merge(a[i],a[i|(1<<j)]))
	ans+=i;
  }
  printf("%lld\n",ans);
  return 0;
}

当然还有玄学的分治

这个做法是在我打比赛的时候hqztrue告诉我的 但那时太弱并不会打

现在填了这个坑 代码大部分参考hqztrue


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#define pb push_back
using namespace std;
typedef long long ll;
typedef vector<int> Vec;

inline char nc(){    
  static char buf[100000],*p1=buf,*p2=buf;    
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }    
  return *p1++;    
}    
    
inline bool read(int &x){    
  char c=nc(),b=1;    
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; else if (c==EOF) return 0;   
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;  return 1;  
}  

const int _M=50000005;

int n,m;
int ls[_M],rs[_M],ncnt;
ll ans;

inline void Ins(int &x,int t,int num){
  x=++ncnt;
  if (t==0) return;
  num&(1<<(t-1))?Ins(rs[x],t-1,num):Ins(ls[x],t-1,num);
}

inline int M(int x,int y){
  if (!x || !y) return x+y;
  int z=++ncnt; ls[z]=M(ls[x],ls[y]); rs[z]=M(rs[x],rs[y]);
  return z;
}

inline void Solve(const Vec& V,int t){
  if(V.size()<2 || t==0) return;
  Vec A,B; int rt=0,tmp=ncnt;
  for(int i=0;i<V.size();i++)
    if(rs[V[i]])
      A.pb(rs[V[i]]),rt=M(M(rt,rs[V[i]]),ls[V[i]]);
    else
      B.pb(ls[V[i]]);
  if (A.size())
    ans+=((ll)A.size()-1)*(1<<(t-1)),Solve(A,t-1),B.pb(rt);
  Solve(B,t-1); ncnt=tmp;
}

int main(){
  int x,t; Vec V;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m);
  for(int i=1;i<=n;i++)
    read(x),Ins(t,m,x),V.pb(t);
  Solve(V,m);
  printf("%lld\n",ans);
  return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值