题目

题目大意

给你一个数列,每个数为[0,65535][0,65535][0,65535]内的整数。

给定一个位运算操作optoptopt,是andandand、ororor、xorxorxor中的一种。

求每个数左边与这个数进行操作的最大值,以及达到最大值的数量。


思考历程

见到这东西,首先想到的当然是Trie了!

显然,把Trie建出来之后,搞定xorxorxor是分分钟的事情。

然而andandand或ororor该怎么搞?

下面用andandand来举例:

从高位往低位做,如果当前位是111就好说,如果是000,那两边都可以走。

首先想到的是先走一边,再走另一边,可是这显然会爆炸……

然后想到的是BFS下去……但是这样依然很慢,因为有可能一直做到最下面才被删掉……

我想到了某天做的一道题目(好像叫做量子纠缠?),开始思考是否可以用并查集的方式来做。

一开始感觉好像可以,用个标记什么的,如果不做就不合并喽~

然后发现打标记不现实,必须要递归下去合并……

但是询问操作有很多个啊……这意味着做完之后还要再恢复到原样,然后再做……

于是时间复杂度就不能保证了:如果数据里有一堆000,那是不是每次都要把每个子树合并一遍?

所以还是不行……

接着我意识到了不能局限于Trie,可能还有别的做法。

然后我就想到了分块!

可以将前888位和后888位拆开,分别搞。

我的做法是将前面888位建Trie。怎么建?我建了282^828个不同的Trie。

这些Trie为忽略一些位建立的(当某个位是000的时候,选什么都没有关系,所以这一位被忽略了)。

然后我在这些Trie的每个叶子节点上都开了一个表,表示后888位是多少时的数量。

就这样,我就打出一个时间复杂度为O(8∗28n)O(8*2^8n)O(8∗28n)的做法,自我感觉良好,好像可以压线过去……

交上去,爆0……后来找到错误,再次交上去,时间超限40……

开了个O3O3O3就有100……


正解

正解的时间复杂度要少个888。这个“常数”决定了40分和100分的差距。

设fi,jf_{i,j}fi,j​表示之前的数的前888位为iii,它的后888位和后888位为jjj的数进行操作的最大值以及对应的数量。

对于每个数xxx,显然可以拆成x=28a+bx=2^8a+bx=28a+b的形式。

询问的时候,枚举iii,用fi,bf_{i,b}fi,b​来更新答案。

修改的时候,枚举jjj,用b opt jb \ opt\ jb opt j来更新fa,jf_{a,j}fa,j​。

程序十分简短,特别好打。


代码

  1. using namespace std;
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. int n,type;
  6. char str[5];
  7. int XOR(int a,int b){return a^b;}
  8. int AND(int a,int b){return a&b;}
  9. int OR(int a,int b){return a|b;}
  10. struct Answer{
  11. int val,num;
  12. inline void update(int _val){
  13. if (_val>val)
  14. val=_val,num=1;
  15. else if (_val==val)
  16. num++;
  17. }
  18. }f[256][256];
  19. inline void solve(int opt(int,int)){//懒得打太多,于是干脆将参数设为函数……
  20. for (int i=1;i<=n;++i){
  21. int x,a,b;
  22. scanf("%d",&x);
  23. a=x>>8,b=x&255;
  24. if (i!=1){
  25. int mx=-1,num=0;
  26. for (int j=0;j<256;++j)
  27. if (f[j][b].num){
  28. int tmp=opt(j,a)<<8|f[j][b].val;
  29. if (tmp>mx)
  30. mx=tmp,num=f[j][b].num;
  31. else if (tmp==mx)
  32. num+=f[j][b].num;
  33. }
  34. if (type)
  35. printf("%d %d\n",mx,num);
  36. else
  37. printf("%d\n",mx);
  38. }
  39. for (int j=0;j<256;++j)
  40. f[a][j].update(opt(j,b));
  41. }
  42. }
  43. int main(){
  44. freopen("binary.in","r",stdin);
  45. freopen("binary.out","w",stdout);
  46. scanf("%d%s%d",&n,str,&type);
  47. if (*str=='x')
  48. solve(XOR);
  49. else if (*str=='a')
  50. solve(AND);
  51. else
  52. solve(OR);
  53. return 0;
  54. }

也把我的那个方法贴上来,开了O3……

  1. #pragma GCC optimize("O3")
  2. #pragma G++ optimize("O3")
  3. using namespace std;
  4. #include <cstdio>
  5. #include <cstring>
  6. #include <algorithm>
  7. int n,type;
  8. char opt[5];
  9. struct Node{
  10. Node *c[2];
  11. } d[14001];
  12. int tor[14001];
  13. int cnt;
  14. Node *root[256];
  15. int rest[14001][256],nr;
  16. inline void insert(int r,int x){
  17. Node *t=root[r];
  18. for (int i=15;i>=8;--i)
  19. if (r>>i-8&1){
  20. if (!t->c[x>>i&1])
  21. t->c[x>>i&1]=&d[++cnt];
  22. t=t->c[x>>i&1];
  23. }
  24. if (!tor[t-d])
  25. tor[t-d]=++nr;
  26. rest[tor[t-d]][x&255]++;
  27. }
  28. int time1,time2;
  29. int main(){
  30. freopen("binary.in","r",stdin);
  31. freopen("binary.out","w",stdout);
  32. scanf("%d%s%d",&n,opt,&type);
  33. for (int i=0;i<256;++i)
  34. root[i]=&d[++cnt];
  35. if (*opt=='x'){
  36. for (int i=1;i<=n;++i){
  37. int x;
  38. scanf("%d",&x);
  39. if (i!=1){
  40. Node *t=root[255];
  41. int ans=0;
  42. for (int j=15;j>=8;--j)
  43. if (t->c[(x>>j&1)^1]){
  44. t=t->c[(x>>j&1)^1];
  45. ans|=1<<j;
  46. }
  47. else
  48. t=t->c[x>>j&1];
  49. int *rst=rest[tor[t-d]],mx=-1,y=x&255;
  50. for (int j=0;j<256;++j)
  51. if (rst[j] && (mx==-1 || (y^j)>mx))
  52. mx=y^j;
  53. printf("%d %d\n",ans|mx,rst[y^mx]);
  54. }
  55. insert(255,x);
  56. }
  57. return 0;
  58. }
  59. for (int i=1;i<=n;++i){
  60. int x;
  61. scanf("%d",&x);
  62. if (i!=1){
  63. int mode=0;
  64. Node *t;
  65. if (*opt=='a')
  66. t=root[mode=x>>8];
  67. else
  68. t=root[mode=x>>8^255];
  69. int ans=0;
  70. for (int j=15;j>=8;--j)
  71. if (mode>>j-8&1){
  72. if (t->c[1]){
  73. t=t->c[1];
  74. ans|=1<<j;
  75. }
  76. else
  77. t=t->c[0];
  78. }
  79. else
  80. ans|=x&1<<j;
  81. int *rst=rest[tor[t-d]],y=x&255,mx=-1,ans2=0;
  82. if (*opt=='a'){
  83. for (int j=0;j<256;++j)
  84. if (rst[j]){
  85. if (mx==-1 || (y&j)>mx)
  86. mx=y&j,ans2=rst[j];
  87. else if ((y&j)==mx)
  88. ans2+=rst[j];
  89. }
  90. if (type==1)
  91. printf("%d %d\n",ans|mx,ans2);
  92. else
  93. printf("%d\n",ans|mx);
  94. }
  95. else{
  96. for (int j=0;j<256;++j)
  97. if (rst[j]){
  98. if (mx==-1 || (y|j)>mx)
  99. mx=y|j,ans2=rst[j];
  100. else if ((y|j)==mx)
  101. ans2+=rst[j];
  102. }
  103. if (type==1)
  104. printf("%d %d\n",ans|mx,ans2);
  105. else
  106. printf("%d\n",ans|mx);
  107. }
  108. }
  109. for (int j=0;j<256;++j)
  110. insert(j,x);
  111. }
  112. return 0;
  113. }

这码量的差距啊……

总结

对于位运算,我们的思想不能再局限于Trie了……

分块还是挺有用的啊……

[JZOJ4616] 【NOI2016模拟7.12】二进制的世界的更多相关文章

  1. 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组

    2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...

  2. 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色

    2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...

  3. 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)

    2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...

  4. 【NOIP2017提高A组模拟9.12】Arrays and Palindrome

    [NOIP2017提高A组模拟9.12]Arrays and Palindrome[SPJ] 题目 Description Input Output Sample Input 1 6 Sample O ...

  5. suse 12 二进制部署 Kubernetets 1.19.7 - 第13章 - 部署metrics-server插件

    文章目录 1.13.0.创建metrics-server证书和私钥 1.13.1.生成metrics-server证书和私钥 1.13.2.开启kube-apiserver聚合配置 1.13.3.分发 ...

  6. suse 12 二进制部署 Kubernetets 1.19.7 - 番外篇 - 增加node节点

    文章目录 0.前景提要 1.准备node节点环境 1.0.修改配置脚本参数 1.1.配置免密 1.2.添加hosts解析 1.3.修改主机名 1.4.更新PATH变量 1.5.安装依赖包 1.6.关闭 ...

  7. suse 12 二进制部署 Kubernetets 1.19.7 - 第01章 - 创建CA证书和kubectl集群管理命令

    文章目录 1.kubernetes集群部署 1.0.创建CA证书和秘钥 1.0.0.安装cfssl工具 1.0.1.创建根证书 1.0.2.创建证书签名请求文件 1.0.3.生成CA证书和秘钥 1.0 ...

  8. suse 12 二进制部署 Kubernetets 1.19.7 - 第02章 - 部署etcd集群

    文章目录 1.2.部署etcd集群 1.2.0.下载etcd二进制文件 1.2.1.创建etcd证书和私钥 1.2.2.生成etcd证书和私钥 1.2.3.配置etcd为systemctl管理 1.2 ...

  9. suse 12 二进制部署 Kubernetets 1.19.7 - 第03章 - 部署flannel插件

    文章目录 1.3.部署flannel网络 1.3.0.下载flannel二进制文件 1.3.1.创建flannel证书和私钥 1.3.2.生成flannel证书和私钥 1.3.3.将pod网段写入et ...

随机推荐

  1. MS Sql添加描述信息 及其他信息

    --查询某个表的描述 SELECT * FROM fn_listextendedproperty (NULL, 'user', 'dbo', 'table', '(表名)',NULL, NULL) - ...

  2. 提升方法(boosting)详解

    提升方法(boosting)详解 提升方法(boosting)是一种常用的统计学习方法,应用广泛且有效.在分类问题中,它通过改变训练样本的权重,学习多个分类器,并将这些分类器进行线性组合,提高分类的性 ...

  3. C# 简单的百度推送代码

    前段时间搞推送来着,安卓方面用到了百度的推送服务,由于只是简单的用到安卓推送的通知功能,所以没用百度推荐的C# SDK,通过借鉴网上的各种资料和百度的API,费了老大劲终于折腾出来一段能用的代码(早知 ...

  4. Servlet(Server Applet) 详解

    Java编写的服务器端程序.其主要功能在于交互式地浏览和修改数据,生成动态Web内容. Servlet的工作模式 客户端发送请求至服务器 服务器启动并调用Servlet,Servlet根据客户端请求生 ...

  5. thinkphp读取配置

    无论何种配置文件,定义了配置文件之后,都统一使用系统提供的C方法(可以借助Config单词来帮助记忆)来读取已有的配置. 获取已经设置的参数值:C('参数名称') 例如, $model = C('UR ...

  6. 异或空间求基(模板)——hdu3949

    输出样例有点问题的.. #include<bits/stdc++.h> using namespace std; #define ll unsigned long long #define ...

  7. 记录下工作中用到的Linux命令

    ---恢复内容开始--- 常用的Linux命令以下命令在博主的开发中经常使用,因此在此做一记录,以做备忘! 1.查看java进程ps -ef|grep javaps aux|grep java lso ...

  8. NX二次开发-算法篇-创建最大边界包容盒

    NX9+VS2012 #include <uf.h> #include <uf_obj.h> #include <uf_modl.h> #include <u ...

  9. NX二次开发-对话框解锁UF_UI_unlock_ug_access

    VC/MFC调用UG Dialog要进入加锁状态 加锁 UF_UI_lock_ug_access ( UF_UI_FROM_CUSTOM ); 此处为UF_UI_select的函数 解锁 UF_UI_ ...

  10. LeetCode 744. Find Smallest Letter Greater Than Target (寻找比目标字母大的最小字母)

    题目标签:Binary Search 题目给了我们一组字母,让我们找出比 target 大的最小的那个字母. 利用 binary search,如果mid 比 target 小,或者等于,那么移到右半 ...