【题目】#2302. 「NOI2017」整数

【题意】有一个整数x,一开始为0。n次操作,加上a*2b,或询问2k位是0或1。\(n \leq 10^6,|a| \leq 10^9,0 \leq b,k \leq 30n\)。

【算法】压位+线段树

【参考】GXZlegend

先考虑以每一位为下标开线段树,将一次加减法拆成log a次一个位的加减法。

考虑对位x加法,如果x为0直接加,如果x为1则向高位找到第一个0加上1,然后之间的区间全部置为0。

减法同理,如果x为1直接减,否则向高位找到第一个1减去,然后区间置1。

线段树维护区间是否全为0/1,复杂度\(O(30n \ \ log^2n)\)(log后面的数字不深究)。

考虑优化,每一位只记录0/1太浪费了,考虑每一位用int记录30位(数据范围有提示作用),这样每次加减就变成两个位的加减了。

按照上面的思路对位x加法,如果\(x<2^{30}-1\)直接加,否则向高位找到第一个满足\(x<2^{30}-1\)的数字加上,区间置为0。减法同理。

复杂度\(O(n \ \ log \ \ n)\)。

注意:

1.每次将修改前后两位数字的方法是分割后和\(2^{30}-1\)取与。

2.线段树二分:①整个区间是否可以跳过,②是否到了叶子,③尝试往左区间,不行再往右区间。

3.重点关注标记的传递,很容易写错。

4.这种题似乎只能静态差错……

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<algorithm>
  4. bool isdigit(char c){return c>='0'&&c<='9';}
  5. int read(){
  6. int s=0,t=1;char c;
  7. while(!isdigit(c=getchar()))if(c=='-')t=-1;
  8. do{s=s*10+c-'0';}while(isdigit(c=getchar()));
  9. return s*t;
  10. }
  11. using namespace std;
  12. const int maxn=1000020,N=1000010,mx=(1<<30)-1,S=(1<<30);
  13. int n,t1,t2,t3;
  14. bool ok;
  15. struct tree{int l,r,c,d,num;}t[maxn*4];
  16. void up(int k){if(t[k<<1].c==t[k<<1|1].c)t[k].c=t[k<<1].c;else t[k].c=-1;}
  17. void modify(int k,int x){t[k].c=t[k].d=x;if(x)t[k].num=mx;else t[k].num=0;}
  18. void down(int k){
  19. if(~t[k].d){
  20. modify(k<<1,t[k].d);modify(k<<1|1,t[k].d);
  21. t[k].d=-1;
  22. }
  23. }
  24. void build(int k,int l,int r){
  25. t[k].l=l;t[k].r=r;t[k].d=-1;t[k].c=0;t[k].num=0;
  26. if(l==r)return;
  27. int mid=(l+r)>>1;
  28. build(k<<1,l,mid);build(k<<1|1,mid+1,r);
  29. }
  30. int query(int k,int x){
  31. if(t[k].l==t[k].r)return t[k].num;
  32. down(k);
  33. int mid=(t[k].l+t[k].r)>>1;
  34. if(x<=mid)return query(k<<1,x);
  35. else return query(k<<1|1,x);
  36. }
  37. void fix(int k,int x,int y){
  38. if(t[k].l==t[k].r){t[k].num=y;t[k].c=-1;if(y==0)modify(k,0);if(y==mx)modify(k,1);return;}
  39. down(k);
  40. int mid=(t[k].l+t[k].r)>>1;
  41. if(x<=mid)fix(k<<1,x,y);else fix(k<<1|1,x,y);
  42. up(k);
  43. }
  44. void find_add(int k,int x){
  45. if(t[k].c==1){modify(k,0);return;}
  46. if(t[k].l==t[k].r){t[k].num++;if(t[k].num==mx)t[k].c=1;else t[k].c=-1;ok=1;return;}
  47. down(k);
  48. int mid=(t[k].l+t[k].r)>>1;
  49. if(x>mid)find_add(k<<1|1,x);else{
  50. find_add(k<<1,x);
  51. if(!ok)find_add(k<<1|1,x);
  52. }
  53. up(k);
  54. }
  55. void find_del(int k,int x){
  56. if(t[k].c==0){modify(k,1);return;}//
  57. if(t[k].l==t[k].r){t[k].num--;if(t[k].num==0)t[k].c=0;else t[k].c=-1;ok=1;return;}//
  58. down(k);
  59. int mid=(t[k].l+t[k].r)>>1;
  60. if(x>mid)find_del(k<<1|1,x);else{//
  61. find_del(k<<1,x);
  62. if(!ok)find_del(k<<1|1,x);
  63. }
  64. up(k);
  65. }
  66. void add(int x,int y){
  67. int z=query(1,x);
  68. fix(1,x,(z+y)%S);
  69. ok=0;
  70. if(z+y>mx)find_add(1,x+1);
  71. }
  72. void del(int x,int y){
  73. int z=query(1,x);
  74. fix(1,x,(z-y+S)%S);
  75. ok=0;
  76. if(z-y<0)find_del(1,x+1);
  77. }
  78. int main(){
  79. n=read();t1=read();t2=read();t3=read();
  80. build(1,1,N);
  81. while(n--){
  82. int kind=read();
  83. if(kind==1){
  84. int a=read(),b=read(),c=b/30;
  85. if(a>0){
  86. add(c+1,(a<<(b%30))&mx);
  87. add(c+2,a>>(30-b%30));
  88. }
  89. else{
  90. a=-a;
  91. del(c+1,(a<<(b%30))&mx);
  92. del(c+2,a>>(30-b%30));
  93. }
  94. }
  95. else{
  96. int x=read();
  97. int y=query(1,x/30+1);
  98. printf("%d\n",(y&(1<<(x%30)))?1:0);
  99. }
  100. }
  101. return 0;
  102. }

【NOI】2017 整数(BZOJ 4942,LOJ2302) 压位+线段树的更多相关文章

  1. 【bzoj4942】[Noi2017]整数 压位+线段树

    题目描述 P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数 $x$ ,一开始为0. 接下来有 $n$ 个操作,每个操作都是以下两种类型中的一种: 1 a b :将 $x$ 加上整数 ...

  2. BZOJ 4942 NOI2017 整数 (压位+线段树)

    题目大意:让你维护一个数x(x位数<=3*1e7),要支持加/减a*2^b,以及查询x的第i位在二进制下是0还是1 作为一道noi的题,非常考验写代码综合能力,敲+调+借鉴神犇的代码 3个多小时 ...

  3. LOJ 2302 「NOI2017」整数——压位线段树

    题目:https://loj.ac/problem/2302 压30位,a最多落在两个位置上,拆成两次操作. 该位置加了 a 之后,如果要进位或者借位,查询一下连续一段 0 / 1 ,修改掉,再在含有 ...

  4. [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并)

    [BZOJ 1483] [HNOI2009] 梦幻布丁 (线段树合并) 题面 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1 ...

  5. [BZOJ 2653] middle(可持久化线段树+二分答案)

    [BZOJ 2653] middle(可持久化线段树+二分答案) 题面 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整. 给你一个长度为n的序 ...

  6. [NOI 2017]整数

    Description 题库链接 P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数 \(x\) ,一开始为 \(0\) . 接下来有 \(n\) 个操作,每个操作都是以下两种类型中 ...

  7. 2017西安区域赛A / UVALive - 8512 线段树维护线性基合并

    题意:给定\(a[1...n]\),\(Q\)次询问求\(A[L...R]\)的异或组合再或上\(K\)的最大值 本题是2017的西安区域赛A题,了解线性基之后你会发现这根本就是套路题.. 只要用线段 ...

  8. bzoj 1537: [POI2005]Aut- The Bus 线段树

    bzoj 1537: [POI2005]Aut- The Bus 先把坐标离散化 设f[i][j]表示从(1,1)走到(i,j)的最优解 这样直接dp::: f[i][j] = max{f[i-1][ ...

  9. BZOJ 1012: [JSOI2008]最大数maxnumber 线段树

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1012 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作.语法:Q L 功能: ...

随机推荐

  1. Linux/Mac 挂载远程服务器目录到本地

    1. 安装 sudo apt-get installsshfs 2. 创建SSHFS 挂载目录 sudo mkdir/mnt/siyuan 3.使用SSHFS 挂载远程的文件系统 sudo sshfs ...

  2. pyinstaller将python编写的打卡程序打包成exe

    编写了一个简易的定时提醒下班打卡程序,python代码如下: #coding:utf-8 import time import datetime from tkMessageBox import * ...

  3. 501. Find Mode in Binary Search Tree【LeetCode by java】

    Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred ...

  4. 如何在一个电脑上同时使用两个Git的账号

    前言 又需要登录公司的账号,又想在电脑上使用自己的账号. 实现 首先是git config方面的设置,要取消掉原本对于git账号的全局设置. git config --global --unset u ...

  5. 【SE】Week1 : 四则运算题目生成器批改器程序总结

    用户需求详见:http://www.cnblogs.com/jiel/p/4810756.html 1)PSP表格分析(预计耗时): PSP2.1 Personal Software Process ...

  6. Sprint 1 Review & Daily Scrum - 11/18

    今天我们组利用课后的时间对Sprint 1阶段进行了回顾,并对接下来的工作进行了安排. Sprint 1阶段我们开始定的计划是完成最基础的背单词功能,可以让用户可以完整地走一遍背单词流程.回顾上周,我 ...

  7. [BUG随想录] expat不兼容BUG

    本周五软工团队项目的第一次前后端全部对接时,出了一个蛋疼的错误. 最初起因是小丽叔出于安全的考虑,使用守护进程来跑Web服务器.守护进程(Daemon)是运行在后台的一种特殊进程,如果服务器用root ...

  8. 使用redis防止抢购商品超卖

    前言: redis不仅仅是单纯的缓存,它还有一些特殊的功能,在一些特殊场景上很好用. 本篇博文用来测试下使用redis来防止抢购商品超卖问题. 内容: 使用redis的list进行测试 思路是设置一个 ...

  9. mybatis之接口方法多参数的三种实现方式

    关键代码举例: DaoMapper.xml <!-- 传入多个参数时,自动转换为map形式 --> <insert id="insertByColumns" us ...

  10. SVN Update Error: Please execute the 'Cleanup' command

    尝试用下面两种方法 svn clean up 中有一个选项break lock勾选上 把对应的文件来里的.svn里面的lock文件删除. svn local delete, incoming dele ...