[BZOJ4942][NOI2017]整数(线段树+压位)

CCF的题经常是对于一个不是非常高级的算法或数据结构挖掘性质进行优化。

松爷的题总是充满常数优化气息,这个题也确实是在常数上做文章。

首先如果全加的话是可以直接暴力的,因为可以证明对每一位来说是均摊$O(1)$的,将a二进制分解一次。

然后将暴力想下去之后容易发现,二进制加法如果进位,肯定是找到这一位之前第一个为0的位加一,然后这两位之间的所有位都变成0(就是模拟竖式加法),减法反之。

于是这个东西就是前驱查找和区间修改,应用线段树解决,$O(n\log^{2}n)$。

但是看到极其相近的数据范围就知道问题不可能这么简单。

回忆高精度的时候用什么方法优化常数:压位。这里也可以这么做。

线段树每一位存的不再是二进制数的一位了,而是60进制数。刚才的所有也全部变成“全0”和“全1”的判断。同时因为$|a|\leq 10^9$,所以最多分解成两位。

关于线段树上求前驱后继的问题,我以前是使用自底向上再向下的方法找,很容易写错,其实可以直接自顶向下查找,因为最多查找失败一次所以复杂度是对的。

不过这题压位听上去和暴力线段树区别不大,细节却是多了几倍。

愚蠢的我只能选择抄JL神犇的代码:http://www.cnblogs.com/RabbitHu/p/UOJ314.html

  1 #include<cstdio>
  2 #include<algorithm>
  3 #define ls (x<<1)
  4 #define rs (ls|1)
  5 #define lson ls,L,mid
  6 #define rson rs,mid+1,R
  7 #define rep(i,l,r) for (int i=l; i<=r; i++)
  8 typedef long long ll;
  9 using namespace std;
 10
 11 const int N=500010,S=60;
 12 const ll P=(1ll<<S)-1;
 13 int m,n,pos[N<<2];
 14 ll a,b,op,tag[N<<2],v[N<<2];
 15 bool all[N<<2][2];
 16
 17 void rd(ll &x){
 18     bool t=0; x=0; char ch=getchar();
 19     while (ch<‘0‘ || ch>‘9‘) t|=(ch==‘-‘),ch=getchar();
 20     while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
 21     if (t) x=-x;
 22 }
 23
 24 void put(int x,ll k){
 25     if (~pos[x]) v[pos[x]]=k;
 26     if (!k) all[x][0]=1,all[x][1]=0,tag[x]=0;
 27     else if (k==P) all[x][0]=0,all[x][1]=1,tag[x]=P;
 28         else all[x][0]=all[x][1]=0,tag[x]=-1;
 29 }
 30
 31 void push(int x){ if (~tag[x]) put(ls,tag[x]),put(rs,tag[x]),tag[x]=-1; }
 32 void upd(int x){ all[x][0]=all[ls][0]&all[rs][0]; all[x][1]=all[ls][1]&all[rs][1]; }
 33
 34 void build(int x,int L,int R){
 35     tag[x]=-1; all[x][0]=1; all[x][1]=0; pos[x]=L;
 36     if (L==R) return; pos[x]=-1;
 37     int mid=(L+R)>>1; build(lson); build(rson);
 38 }
 39
 40 int find(int x,int L,int R,int pos,int k){
 41     if (all[x][!k]) return -1;
 42     if (L==R) return L;
 43     push(x); int mid=(L+R)>>1,tmp;
 44     if (pos<=mid && ~(tmp=find(lson,pos,k))) return tmp;
 45         else return find(rson,pos,k);
 46 }
 47
 48 void mdf(int x,int L,int R,int l,int r,ll k){
 49     if (L==l && r==R){ put(x,k); return; }
 50     push(x); int mid=(L+R)>>1;
 51     if (r<=mid) mdf(lson,l,r,k);
 52     else if (l>mid) mdf(rson,l,r,k);
 53         else mdf(lson,l,mid,k),mdf(rson,mid+1,r,k);
 54     upd(x);
 55 }
 56
 57 ll que(int x,int L,int R,int pos){
 58     if (L==R) return v[L];
 59     push(x); int mid=(L+R)>>1;
 60     if (pos<=mid) return que(lson,pos); else return que(rson,pos);
 61 }
 62
 63 void add(int p,ll k){
 64     ll tmp=que(1,0,n,p); mdf(1,0,n,p,p,(tmp+k)&P);
 65     if (tmp+k>P){
 66         int l=find(1,0,n,p+1,0); mdf(1,0,n,l,l,v[l]+1);
 67         if (p+1<=l-1) mdf(1,0,n,p+1,l-1,0);
 68     }
 69 }
 70
 71 void sub(int p,ll k){
 72     ll tmp=que(1,0,n,p); mdf(1,0,n,p,p,(tmp-k)&P);
 73     if (tmp-k<0){
 74         int l=find(1,0,n,p+1,1); mdf(1,0,n,l,l,v[l]-1);
 75         if (p+1<=l-1) mdf(1,0,n,p+1,l-1,P);
 76     }
 77 }
 78
 79 int main(){
 80     freopen("bzoj4942.in","r",stdin);
 81     freopen("bzoj4942.out","w",stdout);
 82     scanf("%d%*d%*d%*d",&m); n=(m>>1)+4; build(1,0,n);
 83     while (m--){
 84         rd(op);
 85         if (op==1){
 86             rd(a); rd(b);
 87             if (!a) continue;
 88             if (a>0){
 89                 int p=b/S,q=b%S;
 90                 ll x=(a<<q)&P; if (x) add(p,x);
 91                 p++; a>>=S-q; if (b) add(p,a);
 92             }else{
 93                 a=-a; int p=b/S,q=b%S;
 94                 ll x=(a<<q)&P; if (x) sub(p,x);
 95                 p++; a>>=S-q; if (b) sub(p,a);
 96             }
 97         }else rd(a),printf("%lld\n",(que(1,0,n,a/S)>>(a%S))&1);
 98     }
 99     return 0;
100 }

原文地址:https://www.cnblogs.com/HocRiser/p/8977390.html

时间: 2024-11-06 15:55:06

[BZOJ4942][NOI2017]整数(线段树+压位)的相关文章

【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)

[BZOJ4942][Noi2017]整数 题目描述去uoj 题解:如果只有加法,那么直接暴力即可...(因为1的数量最多nlogn个) 先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依次更新这log位,如果最高位依然有进位,那么找到最高位后面的第一个0,将中间的所有1变成0,那个0变成1.这个显然要用到线段树,但是复杂度是nlog2n的,肯定过不去. 于是我在考场上yy了一下,这log位是连续的,我们每次都要花费log的时间去修改一个岂不是很浪费?我们可以先在线段树上找到这段区间

noi2017 T1 整数 ——线段树

loj.ac上有  题目传送门 不过我还是把题目搬过来吧 整数(integer)[题目背景]在人类智慧的山巅,有着一台字长为 1048576 位的超级计算机,著名理论计算机科 学家 P 博士正用它进行各种研究.不幸的是,这天台风切断了电力系统,超级计算机 无法工作,而 P 博士明天就要交实验结果了,只好求助于学过 OI 的你......[题目描述] P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数 x ,一开始为 0. 接下来有 n 个操作,每个操作都是以下两种类型中的一种:

【noi2017】 整数 线段树or模拟

ORZYYB 题目大意:你需要维护一个有$3\times 10^7$个二进制位的数,有一种修改方式和一种询问方式 对这个数加上$a\times2^b$,其中$|a|≤10^9$,$b≤3\times 10^7$,保证需要维护的这个数始终非负 询问这个数第k个二进制位的值 总共有$10^6$次询问/修改操作 我们不难发现,如果只有加法操作的话,对任意一个位执行加法操作,均摊进位次数是1. 证明是显然的(我貌似之前在MC里面用红石电路模拟过二进制进位过程....) 也就是说暴力加暴力进位的复杂度是正

线段树与位运算

线段树的区间修改里有对其区间数全部进行" ^ "或者" | "位运算,我们可以利用这两个位运算的特性来进行修改(这两个都只需记录1的个数即可). “  |   ”:由于它只要二进制上有1则为1,所以我们只需知道它二进制有无1即可.有则改为区间长度个数的1. “  ^  ":由于它二进制上两者不同才为1,所以我们只需知道它二进制有无1即可.有则改为区间长度-之前存在1的个数的1. //https://ac.nowcoder.com/acm/contest/2

poj 2777线段树应用

敲了n遍....RE愉快的debug了一晚上...发现把#define maxn = 100000 + 10 改成 #define maxn = 100010 就过了....感受一下我呵呵哒的表情.... 貌似这个题用了很经典的线段树和位运算.懂了.但不是很懂.确实觉得用的很巧妙.只想说.好坑. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #de

线段树(压位)luogu P1558色板游戏

题目背景 阿宝上学了,今天老师拿来了一块很长的涂色板. 题目描述 色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格.并从左到右标记为1, 2, ... L. 现在色板上只有一个颜色,老师告诉阿宝在色板上只能做两件事: "C A B C" 指在A到 B 号方格中涂上颜色 C. "P A B" 指老师的提问:A到 B号方格中有几种颜色. 学校的颜料盒中一共有 T 种颜料.为简便起见,我们把他们标记为 1, 2, ... T. 开始时色板上原

【弱校胡策】2016.4.14 (bzoj2164)最短路+状压DP+矩阵乘法+高斯消元+树链剖分+线段树+背包DP

cyyz&qhyz&lwyz&gryz弱校胡策 命题人:cyyz ws_fqk T3暴力写挫了 50+10+0滚粗辣! 奇妙的约会(appointment.cpp/c/pas) [问题描述] DQS和sxb在网上结识后成为了非常好的朋友,并且都有着惊人 的OI水平.在NOI2333的比赛中,两人均拿到了金牌,并保送进入 HU/PKU.于是两人决定在这喜大普奔的时刻进行面基. NOI2333参赛选手众多,所以安排了n个考点,DQS在1号考点, 而sxb在n号考点.由于是举办全国性赛事

NOIP2017整数 【线段树】

题目 题目背景 在人类智慧的山巅,有着一台字长为10485761048576 位(此数字与解题无关)的超级计算机,著名理论计算机科 学家P博士正用它进行各种研究.不幸的是,这天台风切断了电力系统,超级计算机 无法工作,而 P 博士明天就要交实验结果了,只好求助于学过OI的你. . . . . . 题目描述 P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数xx ,一开始为00 . 接下来有nn 个操作,每个操作都是以下两种类型中的一种: 1 a b:将xx 加上整数a\cdot

ACM: FZU 2105 Digits Count - 位运算的线段树【黑科技福利】

FZU 2105  Digits Count Time Limit:10000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Practice Description Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations: Operation 1: AND opn L R Here opn, L and R are integer