Acwing 2556. 第八大奇迹(整体二分)

    科技2026-03-19  8

    题意:

    有一个长度为n的序列a,一开始所有数为0, q次操作,操作有两种: (C x v):将a[x]修改为v (Q l r):问[l,r]中第8大的数

    数据范围:n,q<=1e5,0<=a(i)<=1e9

    解法:

    问题可以离线,因此可以整体二分。

    ps: 这题树套树也可以做,但是整体二分省空间

    code:

    #include<bits/stdc++.h> using namespace std; const int maxm=2e5+5; struct QQ{ int op,x,y,k,id; }Q[maxm],lc[maxm],rc[maxm]; int mark[maxm]; int ans[maxm]; int c[maxm]; int n,q; int lowbit(int i){ return i&-i; } void add(int i,int t){ while(i<=n){ c[i]+=t,i+=lowbit(i); } } int ask(int i){ int ans=0; while(i){ ans+=c[i],i-=lowbit(i); } return ans; } void solve(int l,int r,int ql,int qr){ if(ql>qr)return ; if(l==r){ for(int i=ql;i<=qr;i++){ if(Q[i].op==2)ans[Q[i].id]=l; } return ; } int mid=(l+r)>>1; int cnt1=0,cnt2=0; for(int i=ql;i<=qr;i++){ if(Q[i].op==1){ if(Q[i].y<=mid)lc[++cnt1]=Q[i];//[st,mid] else add(Q[i].x,Q[i].k),rc[++cnt2]=Q[i];//[mid+1,ed] }else if(Q[i].op==2){ int t=ask(Q[i].y)-ask(Q[i].x-1); if(t>=Q[i].k)rc[++cnt2]=Q[i]; else Q[i].k-=t,lc[++cnt1]=Q[i]; } } for(int i=ql;i<=qr;i++){//clear if(Q[i].op==1&&Q[i].y>mid){ add(Q[i].x,-Q[i].k); } } for(int i=1;i<=cnt1;i++){ Q[ql+i-1]=lc[i]; } for(int i=1;i<=cnt2;i++){ Q[ql+cnt1+i-1]=rc[i]; } solve(l,mid,ql,ql+cnt1-1); solve(mid+1,r,ql+cnt1,qr); } signed main(){ scanf("%d%d",&n,&q); int tot=0; int q1=0; for(int i=1;i<=q;i++){ char op[3];scanf("%s",op); int x,y;scanf("%d%d",&x,&y); if(op[0]=='C'){//修改 if(mark[x]){ Q[++tot]={1,x,mark[x],-1,666};//删掉之前的 } Q[++tot]={1,x,y,1,666}; mark[x]=y; }else{//查询 Q[++tot]={2,x,y,8,++q1}; } } solve(0,1e9,1,tot); for(int i=1;i<=q1;i++){ printf("%d\n",ans[i]); } return 0; }
    Processed: 0.015, SQL: 9