【noi2017】 整数 线段树or模拟

ORZYYB

题目大意:你需要维护一个有$3\times 10^7$个二进制位的数,有一种修改方式和一种询问方式

对这个数加上$a\times2^b$,其中$|a|≤10^9$,$b≤3\times 10^7$,保证需要维护的这个数始终非负

询问这个数第k个二进制位的值

总共有$10^6$次询问/修改操作

我们不难发现,如果只有加法操作的话,对任意一个位执行加法操作,均摊进位次数是1。

证明是显然的(我貌似之前在MC里面用红石电路模拟过二进制进位过程。。。。)

也就是说暴力加暴力进位的复杂度是正确的。

但是这里有a并不保证非负,这样一来通过精心(大雾)的构造方式,可以让你疯狂进位/退位,所以并不能单纯暴力模拟。

我们考虑对加法部分和减法部分分开维护(设A为加法的部分,B为减法的部分),这样的进位复杂度显然就是对的。

考虑到$A≥B$,那么显然有$\frac{A}{2^k}≥\frac{B}{2^k}$。

对于每次查询操作,我们分别找出A的第k位和B的第k位

现在对答案会产生影响的显然是A的末k-1位和B的末k-1位相减后,A的第k位是否需要退位。

我们考虑开一个set,若A的第i位和B的第i位不同,那么我们就把i丢入set中。

我们考虑在set中找到满足<k的i,直接判断A的第i位和B的第i位的大小关系,就可以判出是否会产生退位。

set的维护在对大数做修改的时候去更新。

考虑到这个数非常大,直接维护会超时,我们不妨做一波压位,然后再来维护,这样就跑得快很多了。

注意!对于一个32位的数,左移32位的操作会直接被忽略。

当然之前还有一些比较菜的想法,维护整个数的差分序列,若差分序列某个位置不为0就丢入set中,然后也来压位一波,不过代码估计长很多。

时间复杂度:$O(n\ log\ n)$

 1 #include<bits/stdc++.h>
 2 #define M 500005
 3 #define S 64
 4 #define L unsigned long long
 5 using namespace std;
 6
 7 int n,t1,t2,t3;
 8 L a[M]={0},b[M]={0},P=-1;
 9 set<int> s;
10
11 void upd(int x){
12     if(a[x]!=b[x]) s.insert(x);
13     else{
14         if(s.find(x)!=s.end()) s.erase(x);
15     }
16 }
17
18 int main(){
19     scanf("%d%d%d%d",&n,&t1,&t2,&t3);
20     while(n--){
21         int op,B; scanf("%d",&op); L A;
22         int aa;
23         if(op==1){
24             scanf("%d%d",&aa,&B);
25             if(aa>0){
26                 A=aa;
27                 int x=B/S,y=B%S;
28                 L s1=(A<<y)&P,s2=y?(A>>(S-y)):0;
29                 a[x]=a[x]+s1; upd(x);
30                 if(a[x]<s1) s2++;
31                 while(s2){
32                     x++;
33                     a[x]=a[x]+s2; upd(x);
34                     s2=(a[x]<s2);
35                 }
36             }else{
37                 A=-aa;
38                 int x=B/S,y=B%S;
39                 L s1=(A<<y)&P,s2=y?(A>>(S-y)):0;
40                 b[x]=b[x]+s1; upd(x);
41                 if(b[x]<s1) s2++;
42                 while(s2){
43                     x++;
44                     b[x]=b[x]+s2; upd(x);
45                     s2=(b[x]<s2);
46                 }
47             }
48         }else{
49             scanf("%d",&B);
50             int x=B/S,y=B%S;
51             int ans=(((a[x]>>y)^(b[x]>>y))&1);
52             A=y?(a[x]<<(S-y)):0;
53             L BB=y?(b[x]<<(S-y)):0;
54             if(A<BB) ans^=1;
55             if(A==BB){
56                 set<int>::iterator it=s.lower_bound(x);
57                 if(it!=s.begin()){
58                     it--; x=*it;
59                     if(a[x]<b[x]) ans^=1;
60                 }
61             }
62             printf("%d\n",ans);
63         }
64     }
65 }

原文地址:https://www.cnblogs.com/xiefengze1/p/11005893.html

时间: 2024-10-08 11:03:22

【noi2017】 整数 线段树or模拟的相关文章

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

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

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

CCF的题经常是对于一个不是非常高级的算法或数据结构挖掘性质进行优化. 松爷的题总是充满常数优化气息,这个题也确实是在常数上做文章. 首先如果全加的话是可以直接暴力的,因为可以证明对每一位来说是均摊$O(1)$的,将a二进制分解一次. 然后将暴力想下去之后容易发现,二进制加法如果进位,肯定是找到这一位之前第一个为0的位加一,然后这两位之间的所有位都变成0(就是模拟竖式加法),减法反之. 于是这个东西就是前驱查找和区间修改,应用线段树解决,$O(n\log^{2}n)$. 但是看到极其相近的数据范

HDU 4942 Game on S♂play(线段树、模拟、扩栈)

比赛的时候想到这题的大概做法,但由于卡别的水题...就赛后做了... 题意:给一个二叉树,每个结点有一个w[i],有3种操作,0 x表示左旋x,1 x表示右旋x,3 x表示询问x结点的价值,其中,价值为x子树结点的累加价值的累乘,其中,结点的累加价值为结点子树的Σw[i].即询问是,∏Σw. 好像题意被我说得好渣好乱....还是忽略上面2行吧... 首先,左旋右旋不影响中序遍历的index,即,可以把题目那2个图进行中序遍历,结果都是αXβYγ.由此,可以建立线段树. 而左旋右旋的过程模拟即可,

noi2017 T1 整数 ——线段树

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

Vijos P1103 校门外的树【线段树,模拟】

校门外的树 描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……,L,都种有一棵树. 由于马路上有一些区域要用来建地铁.这些区域用它们在数轴上的起始点和终止点表示. 已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分.现在要把这些区域中的树(包括区域端点处的两棵树)移走.你的任务是计算将这些树都移走后,马路上还有多少棵树. 格式 输入格式 输入的

hdu 4902 线段树+逆向模拟

http://acm.hdu.edu.cn/showproblem.php?pid=4902 出n个数,然后对这n个数进行两种操作: 如果是 1 l r x,则把 [l, r] 区间里面的每一个数都变为x: 如果是 2 l r x,则 比较 [l, r]区间里的数a_i和x的大小,如果a_i > x,把a_i变为a_i和x的最大公约数. 最后输出这n个数最终的值. 线段树可搞...但是没必要!... 线段树,每个结点多一个lazy表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候

USACO Overplanting ( 线段树扫描线 )

题意 : 在二维平面上给出 N 个矩形,问你所有矩形构成的图案的面积是多少(相互覆盖的地方只计算一次) 分析 :  求矩形面积并可以模拟来做,不过使用线段树来辅助做扫描线可以更高效地求解 扫描线顾名思义就是类似有一条线在二维平面上扫过去,将矩形面积并给扫出来 实现是使用线段树来模拟这个扫描的过程 第一步就是确定扫描的方向,是从左到右扫还是从上到下扫,这里以从上到下为例 第二步就是确定题目的坐标是否可能很大,如果很大意味着线段树开不了,则要进行离散化操作 由于是从上到下,我们记录每个矩形的上下两条

POJ2886 Who Gets the Most Candies? 【线段树】+【单点更新】+【模拟】+【反素数】

Who Gets the Most Candies? Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 9416   Accepted: 2868 Case Time Limit: 2000MS Description N children are sitting in a circle to play a game. The children are numbered from 1 to N in clockwise o

【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少.1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20 Sample Input 9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3 Sample Output 17 25