[关闭]
@ysner 2018-07-23T18:42:31.000000Z 字数 1535 阅读 2215

最大值

贪心 Tire


题面

给定个数和,求两数间最大运算值。

数据范围

解析

算法

枚举

算法

注意到数的个数对答案没有影响。
开个桶存这个数。
然后枚举。
(注意答案可能为两个相等数的运算值)

与运算

从高位开始枚举,查询该位为的数有个。
如果,说明这一位凑不出,答案这一位为
如果,说明这一位只能由两数凑出,答案就是这两数的运算值。
如果,说明答案这一位为,同时可以把这一位为的数排除掉,因对答案无贡献。

  1. fp(i,1,n) vis[i]=1;
  2. fq(i,20,0)
  3. {
  4. top=0;
  5. fp(j,1,n)
  6. if(vis[j])
  7. if(a[j]&(1<<i)) sta[++top]=a[j];
  8. if(top<2) continue;
  9. if(top==2) {ans=sta[1]&sta[2];return;}
  10. if(top>2) ans|=(1<<i);
  11. fp(j,1,n)
  12. if(!(a[j]&(1<<i))) vis[j]=0;
  13. }

异或运算

对于一个数,我们很清楚使运算值最大的另一数是多少。
于是从高位往低位构建一棵树(二叉树)。
显然无脑尽量走更优边。

  1. il void Build(re int x,re int d,re int num)
  2. {
  3. if(d<0) return;
  4. if(num&(1<<d))
  5. {
  6. if(!t[x][1]) t[x][1]=++tot;
  7. Build(t[x][1],d-1,num);
  8. }
  9. else
  10. {
  11. if(!t[x][0]) t[x][0]=++tot;
  12. Build(t[x][0],d-1,num);
  13. }
  14. }
  15. il int dfs(re int x,re int now,re int d)
  16. {
  17. if(d<0) return 0;
  18. if(now&(1<<d))
  19. {
  20. if(t[x][0]) return (1<<d)|dfs(t[x][0],now,d-1);
  21. if(t[x][1]) return dfs(t[x][1],now,d-1);
  22. }
  23. else
  24. {
  25. if(t[x][1]) return (1<<d)|dfs(t[x][1],now,d-1);
  26. if(t[x][0]) return dfs(t[x][0],now,d-1);
  27. }
  28. return 0;
  29. }
  30. il void work2()
  31. {
  32. fp(i,1,n) Build(0,20,a[i]);
  33. fp(i,1,n) ans=max(ans,dfs(0,a[i],20));
  34. }

或运算

首先标记每个数本身和真子集(即本身改几个后所形成的数),代表这些数能对结果提供的有效(即另一数对应位为,而不是)影响。
具体方法是先标记本身。
然后枚举数位,从大到小枚举数,把该位为的数的这一位改为后形成的数标记。

接下来,对每个数从高位往低位枚举,并设一补集
如果第位为,且这一数可被提供出来,则
其实也是贪心。

  1. memset(tong,0,sizeof(tong));
  2. fp(i,1,n) tong[a[i]]=1;
  3. fp(i,0,20)
  4. fq(j,1<<20,0)
  5. if(j&(1<<i)) tong[j^(1<<i)]|=tong[j];
  6. fp(j,1,n)
  7. {
  8. re int E=0;
  9. fq(i,20,0)
  10. if(!(a[j]&(1<<i))&&tong[E|(1<<i)]) E|=(1<<i);
  11. ans=max(ans,E|a[j]);
  12. }

算法

以上三算法复杂度均为
于是一道模板题就被了。
然而我在考场上犯了好多逗逼错误

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注