BZOJ4942 [Noi2017]整数
题解:
这题有点卡常数(自己的常数大)
每30位压成一个数
思路1:
每次操作拆成最多两个位置的操作
每加一次最多会进1
每减一次最多会退1
然后用线段树维护最近的非0位和非满位
然后就是区间赋值和单点修改
好难写没写出来QWQ
思路2:
分两个数组记录加操作(A)和减操作(B)
用set维护所有的不同位置
比较两个字符串的大小即找到第一个不同的位置
查询时分情况讨论
A的这一位是1还是0,B的这一位是1还是0,以及AB的后缀大小情况
讨论进位退位即可
暴力修改即可
#include<iostream> #include<cstdio> #include<cstring> #include<set> using namespace std; const int u=30; const int ux=(1<<u); typedef long long Lint; const int maxn=2000009; int n; int TT,t1,t2,t3; Lint A[maxn]={0}; Lint B[maxn]={0}; set<int>S; int cmp(int x){ set<int>::iterator it=S.upper_bound(x); if(it==S.begin())return 0; --it; if(A[*it]>B[*it])return 1; if(A[*it]<B[*it])return -1; } void pushup(int bol){ if(A[bol]==B[bol]){ if(S.count(bol))S.erase(bol); } if(A[bol]!=B[bol]){ if(!S.count(bol))S.insert(bol); } } void print(Lint x){ if(x==0)return; print(x>>1); if(x&1)printf("1"); else printf("0"); } int main(){ scanf("%d%d%d%d",&TT,&t1,&t2,&t3); n=TT+1000; while(TT--){ int opty,y; Lint x; scanf("%d",&opty); if(opty==1){ scanf("%lld%d",&x,&y); ++y; int bol=(y-1)/u+1; y-=(bol-1)*u; if(x>0){ A[bol]+=(x<<(y-1)); while(A[bol]>=ux){ A[bol+1]+=A[bol]/ux; A[bol]%=ux; pushup(bol); ++bol; } pushup(bol); }else{ x=-x; B[bol]+=(x<<(y-1)); while(B[bol]>=ux){ B[bol+1]+=B[bol]/ux; B[bol]%=ux; pushup(bol); ++bol; } pushup(bol); } }else{ scanf("%d",&y); ++y; int bol=(y-1)/u+1; y-=(bol-1)*u; Lint tm1=A[bol]&((1<<(y-1))-1); Lint tm2=B[bol]&((1<<(y-1))-1); int tm3=cmp(bol-1); int r=0; if(tm1==tm2){ r=tm3; }else if(tm1<tm2){ r=-1; }else{ r=1; } if(A[bol]&(1<<(y-1))){ if(B[bol]&(1<<(y-1))){ if(r<0)printf("1\n"); else printf("0\n"); }else{ if(r>=0)printf("1\n"); else printf("0\n"); } }else{ if(B[bol]&(1<<(y-1))){ if(r>=0)printf("1\n"); else printf("0\n"); }else{ if(r<0)printf("1\n"); else printf("0\n"); } } } } return 0; }
致歉:笔者已经意识到这是一篇几乎没有价值的文章,给您的阅读带来不好的体验,并且干扰了您的搜索环境,非常抱歉!