【noi2017】 整数 线段树or模拟

ORZYYB

 

题目大意:你需要维护一个有3×107个二进制位的数,有一种修改方式和一种询问方式

对这个数加上a×2b,其中|a|109b3×107保证需要维护的这个数始终非负

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

总共有106次询问/修改操作

 

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

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

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

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

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

 

考虑到AB,那么显然有A2kB2k

对于每次查询操作,我们分别找出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 }
复制代码
posted @   AlphaInf  阅读(362)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
历史上的今天:
2018-06-11 【PKUSC2018】【loj6433】最大前缀和 状压dp
点击右上角即可分享
微信分享提示