UOJ#314. 【NOI2017】整数 其他
作者:互联网
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ314.html
题解
如果只加不减,那么瞎势能分析一波可以知道暴力模拟的复杂度是对的。
但是有减法怎么办???
再搞一个类似的,维护减了多少。
那么,询问一个数位的值的时候,我们只需要得到两部分值中这一位的值是多少,以及是否退位,就可以得到答案。
显然关键是退不退位。
退不退位看这一位之后的后缀部分哪一个大。
这里我们需要这样做: 如果加法和减法两部分维护的值中,某一位都不是 0 ,那么就两边互相抵消,直到两边至少有一个是 0 。
那么判断哪一个大就是看两部分中,当前位以后,第一个有值的位置是哪一个大。用两个set瞎搞就好了。
我们维护的时候用 $2^{30}$ 进制,这样时间复杂度就可以接受了。
时间复杂度 $O(n\log n)$ 。
代码
#include <bits/stdc++.h> #define clr(x) memset(x,0,sizeof (x)) #define y1 __zzd001 using namespace std; typedef long long LL; LL read(){ LL x=0,f=0; char ch=getchar(); while (!isdigit(ch)) f|=ch=='-',ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } const int N=(1<<20)+5; int n,t1,t2,t3,base=1<<30; int v[2][N]; set <int> s[2]; void upd1(int a,int b){ if (!a) return; if (v[1][b]){ int d=min(v[1][b],a); v[1][b]-=d,a-=d; if (!v[1][b]) s[1].erase(b); } if (!a) return; if (!v[0][b]) s[0].insert(b); v[0][b]+=a; if (v[0][b]>=base){ if (!(v[0][b]-=base)) s[0].erase(b); upd1(1,b+1); } } void upd2(int a,int b){ if (!a) return; if (v[0][b]){ int d=min(v[0][b],a); v[0][b]-=d,a-=d; if (!v[0][b]) s[0].erase(b); } if (!a) return; if (!v[1][b]) s[1].insert(b); v[1][b]+=a; if (v[1][b]>=base){ if (!(v[1][b]-=base)) s[1].erase(b); upd2(1,b+1); } } int main(){ n=read(),t1=read(),t2=read(),t3=read(); clr(v); s[0].clear(),s[1].clear(); s[0].insert(-1),s[1].insert(-1); while (n--){ int type=read(); if (type==1){ int a=read(),b=read(); int c=b%30,d=b/30; if (a>=0){ upd1((a<<c)&(base-1),d); upd1(a>>(30-c),d+1); } else { a=-a; upd2((a<<c)&(base-1),d); upd2(a>>(30-c),d+1); } } else { int k=read(); int a=(v[0][k/30]>>(k%30)&1)^(v[1][k/30]>>(k%30)&1); int x=v[0][k/30]&((1<<(k%30))-1); int y=v[1][k/30]&((1<<(k%30))-1); if (x!=y){ if (x<y) a^=1; } else if (*--s[0].lower_bound(k/30)<*--s[1].lower_bound(k/30)) a^=1; printf("%d\n",a); } } return 0; }
标签:insert,ch,return,int,30,314,read,NOI2017,UOJ 来源: https://www.cnblogs.com/zhouzhendong/p/UOJ314.html