[关闭]
@ysner 2018-08-10T14:11:44.000000Z 字数 4339 阅读 1687

mst

图论


题面

给一个点完全图,点权均小于。定义边权等于两端点点权的与和(即)。求最大生成树边权和。

解析

算法

搞复杂度为,会
可以用算法。

  1. for(rg int i=1;i<=n;i++) Dis[i]=-Inf;
  2. Dis[1]=0; ll Ans=0;
  3. for(rg int i=1;i<=n;i++)
  4. { int Now=0,Maxd=-Inf;
  5. for(rg int j=1;j<=n;j++)
  6. if(!Blue[j]&&Dis[j]>Maxd) Now=j,Maxd=Dis[j];
  7. Ans+=Maxd,Blue[Now]=1;
  8. for(rg int j=1;j<=n;j++)
  9. if(!Blue[j]) Dis[j]=Max(Dis[j],A[Now]&A[j]);
  10. }

值得注意的是初始值极小,永远在取

算法

说明点权只能为
点最多能连条边。

算法

注意到如果数值重复出现次,答案加上(相同数值相互连边)后,就可把该数值视为唯一。
于是降到,又可以啦。

算法

注意到若,在间连边一定最优。于是可以找出每个点的子集(复杂度),若存在,优先建边并统计答案。

然后由于剩下点互不包含,点数会降到(因为最多的情况是选所有有的二进制数,而这些数因为数量相同,肯定互不包含),似乎又可以暴力了。

算法

这个算法很神仙。

论如何求集合中选一个数与当前值进行位运算的
(当然先CJrank1yyb)

如果是暴力的话,我们的方法有两种:

两种方法一种是插入,询问,另外一个是插入,询问
我们把两种东西结合一下,这样可以得到一个的方法。

我们对于每个数从中间分开,拆成前个二进制位和后个二进制位。
这样子我们可以预处理一个数组
表示集合中一个前位是的数,后位与进行位运算的最大值。

因为位运算是可以按位贪心的,所以对于查询一个数,我们把它拆成
每次先暴力所有可能的前位,找到与能够构成最大值的那些数,然后对于找到的所有数的前八位,直接查
因为前八位更大的数一定更大,那么影响结果的就只剩下后八位了,
把两个部分拼接起来就好了。
这样子暴力前八位的复杂度是,查找后面部分最大值的复杂度是
所以这样子总的复杂度
而对于集合中插入一个数,前位唯一确定,每次只需要预处理后位的结果。
时间复杂度还是,总的复杂度还是
最后记得反过来再跑一遍。

照搬该方法到本题,复杂度为
注意一下连边。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<vector>
  8. #define re register
  9. #define il inline
  10. #define ll long long
  11. #define max(a,b) ((a)>(b)?(a):(b))
  12. #define min(a,b) ((a)<(b)?(a):(b))
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int mod=1e9+7,N=1e5+100;
  17. int a[N],h[N],cnt,n,m,k,f[N],p=1,base,nxt[N],t[1<<11][1<<11],c[1<<11][1<<11],cp[N],tl[N],ma[N],fa[N];
  18. ll ans;
  19. il ll gi()
  20. {
  21. re ll x=0,t=1;
  22. re char ch=getchar();
  23. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  24. if(ch=='-') t=-1,ch=getchar();
  25. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  26. return x*t;
  27. }
  28. il int find(re int x){return fa[x]?fa[x]=find(fa[x]):x;}
  29. il void link(re int u,re int v)
  30. {
  31. re int fu=find(u),fv=find(v);
  32. if(fu^fv)
  33. {
  34. nxt[tl[fv]]=h[fu];
  35. tl[fv]=tl[fu];
  36. fa[fu]=fv;
  37. ++p;
  38. }
  39. }
  40. il void solve()
  41. {
  42. memset(f,0,sizeof(f));memset(t,-1,sizeof(t));memset(ma,-1,sizeof(ma));
  43. fp(i,1,n)
  44. if(!fa[i])
  45. {
  46. for(re int j=h[i];j;j=nxt[j])
  47. {
  48. re int x=a[j]>>k,y=a[j]&base;//x代表前一半位,y代表后一半位
  49. fp(z,0,(1<<m-k)-1)//枚举前一半位并统计答案
  50. if(f[z])
  51. {
  52. re int ss=a[j]&t[z][y]|(z<<k&a[j]);//组合
  53. if(ss>ma[i]) ma[i]=ss,cp[i]=c[z][y];
  54. }
  55. }
  56. for(re int j=h[i];j;j=nxt[j])
  57. {
  58. re int x=a[j]>>k,y=a[j]&base;f[x]=1;//标记其存在
  59. fp(z,0,(1<<k)-1)//枚举后一半位并处理
  60. if((y&z)>t[x][z]) t[x][z]=y&z,c[x][z]=i;
  61. }
  62. }
  63. memset(f,0,sizeof(f));memset(t,0,sizeof(t));
  64. fq(i,n,1)
  65. if(!fa[i])
  66. {
  67. for(re int j=h[i];j;j=nxt[j])
  68. {
  69. re int x=a[j]>>k,y=a[j]&base;
  70. fp(z,0,(1<<m-k)-1)
  71. if(f[z])
  72. {
  73. re int ss=a[j]&t[z][y]|(z<<k&a[j]);
  74. if(ss>ma[i]) ma[i]=ss,cp[i]=c[z][y];
  75. }
  76. }
  77. for(re int j=h[i];j;j=nxt[j])
  78. {
  79. re int x=a[j]>>k,y=a[j]&base;f[x]=1;
  80. fp(z,0,(1<<k)-1)
  81. if((y&z)>t[x][z]) t[x][z]=y&z,c[x][z]=i;
  82. }
  83. }
  84. fp(i,1,n)
  85. if(!fa[i])
  86. {
  87. re int u=find(i),v=find(cp[i]);
  88. if(u^v) ans+=ma[i],link(i,cp[i]);
  89. }
  90. }
  91. int main()
  92. {
  93. freopen("mst.in","r",stdin);
  94. freopen("mst.out","w",stdout);
  95. n=gi();m=gi();k=m/2;base=(1<<k)-1;
  96. fp(i,1,n)
  97. {
  98. a[i]=gi();
  99. h[i]=tl[i]=i;
  100. }
  101. while(p<n) solve();
  102. printf("%lld\n",ans);
  103. fclose(stdin);
  104. fclose(stdout);
  105. return 0;
  106. }

算法

倒序枚举用于连接的边权,用表示的最大超集(只用多一个)所在联通块的代表元。枚举的另一超集(只用多一个),若两者不在同一联通块,合并之。从大到小保证最优性。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<vector>
  8. #define re register
  9. #define il inline
  10. #define ll long long
  11. #define max(a,b) ((a)>(b)?(a):(b))
  12. #define min(a,b) ((a)<(b)?(a):(b))
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int mod=1e9+7,N=1e5+100;
  17. int a[1<<21],h[N],cnt,n,m,f[1<<21];
  18. ll ans;
  19. il ll gi()
  20. {
  21. re ll x=0,t=1;
  22. re char ch=getchar();
  23. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  24. if(ch=='-') t=-1,ch=getchar();
  25. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  26. return x*t;
  27. }
  28. il int find(re int x){return f[x]?f[x]=find(f[x]):x;}
  29. int main()
  30. {
  31. freopen("mst.in","r",stdin);
  32. freopen("mst.out","w",stdout);
  33. n=gi();m=gi();
  34. fp(i,1,n)
  35. {
  36. re int x=gi();
  37. if(a[x]) ans+=x;
  38. a[x]=x;
  39. }
  40. fq(t,(1<<m)-1,0)
  41. {
  42. re int &u=a[t];
  43. for(re int i=0;!u&&i<m;i++) u=a[t|(1<<i)];
  44. if(!u) continue;
  45. fp(i,0,m-1)
  46. {
  47. re int v=a[t|(1<<i)],fu=find(u),fv=find(v);
  48. if(v&&fu!=fv) ans+=t,f[fv]=fu;
  49. }
  50. }
  51. printf("%lld\n",ans);
  52. fclose(stdin);
  53. fclose(stdout);
  54. return 0;
  55. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注