1. 程式人生 > >[SCOI2014]方伯伯的OJ(線段樹)

[SCOI2014]方伯伯的OJ(線段樹)

printf color 移動 ios 心情 ret 應該 cst 線段樹

方伯伯正在做他的Oj。現在他在處理Oj上的用戶排名問題。Oj上註冊了n個用戶,編號為1~n“,一開始他們按照編號排名。

方伯伯會按照心情對這些用戶做以下四種操作,修改用戶的排名和編號:

1.操作格式為1 x y,意味著將編號為x的用戶編號改為y,而排名不變,執行完該操作後需要輸出該用戶在隊列中的位置,數據保證x必然出現在隊列中,同時,1是一個當前不在排名中的編號。

2.操作格式為2 x,意味著將編號為x的用戶的排名提升到第一位,執行完該操作後需要輸出執行該操作前編號為x用戶的排名。

3.操作格式為3 x,意味著將編號為x的用戶的排名降到最後一位,執行完該操作後需要輸出執行該操作前編號為x用戶的排名。

4.操作格式為4 k,意味著查詢當前排名為k的用戶編號,執行完該操作後需要輸出當前操作用戶的編號。

但同時為了防止別人監聽自己的工作,方伯伯對他的操作進行了加密,即將四種操作的格式分別改為了:

  • 1 x+a y+a
  • 2 x+a
  • 3 x+a
  • 4 k+a
  • 其中a為上一次操作得到的輸出,一開始a=0。

例如:上一次操作得到的輸出是5這一次操作的輸入為:1 13 15因為這個輸入是經過加密後的,所以你應該處理的操作是1 8 10現在你截獲了方伯伯的所有操作,希望你能給出結果。

Solution

調了一晚上。。。菜的不行。

因為數列每次只會變化一個數,所以可以不用splay或fhqtreap,用一顆動態開點線段樹就可以完成。

因為我們的移動只關於先插和後插,所以我們線段樹的區間為m+n+m對於一個移動,我們先把該點在對應位置刪除,再將該數插入前/後對應位置。

與此同時,我們開兩個map,一個儲存編號對應的位置,一個儲存位置對應的編號。

Code

#include<iostream>
#include<cstdio>
#include<map>
#define ls tr[cnt].l
#define rs tr[cnt].r
#define N 200002
using namespace std;
map<int,int>mp,anti_mp;
int tot,id,ans,n,m,root,h,t,x,y;
struct seg{ int l,r,num; bool la; }tr[N*20]; inline int rd(){ int x=0;char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x; } inline void pushdown(int cnt,int l1,int l2){ if(!ls)ls=++tot;if(!rs)rs=++tot; tr[ls].num=l1;tr[rs].num=l2; tr[cnt].la=0;tr[ls].la=tr[rs].la=1; } void add(int &cnt,int l,int r,int x,int tag,int rk){ if(!cnt)cnt=++tot; if(l==r){ if(tag<0)ans=rk+1,anti_mp.erase(l); else anti_mp[l]=id; tr[cnt].num-=tag; return; } int mid=(l+r)>>1; if(tr[cnt].la)pushdown(cnt,mid-l+1,r-mid); if(mid>=x)add(ls,l,mid,x,tag,rk); else add(rs,mid+1,r,x,tag,rk+mid-l+1-tr[ls].num); tr[cnt].num=tr[ls].num+tr[rs].num; } void find(int &cnt,int l,int r,int rk){ if(!cnt)cnt=++tot; if(l==r){ if(anti_mp.find(l)!=anti_mp.end())ans=anti_mp[l];else ans=l-m; return; } int mid=(l+r)>>1; if(tr[cnt].la)pushdown(cnt,mid-l+1,r-mid); int num=mid-l+1-tr[ls].num; if(num>=rk)find(ls,l,mid,rk); else find(rs,mid+1,r,rk-num); } void gai(int &cnt,int l,int r,int L,int R){ if(!cnt)cnt=++tot; if(l>=L&&r<=R){ tr[cnt].num=r-l+1; tr[cnt].la=1; return; } int mid=(l+r)>>1; if(mid>=L)gai(ls,l,mid,L,R); if(mid<R)gai(rs,mid+1,r,L,R); tr[cnt].num=tr[ls].num+tr[rs].num; } void upd(int &cnt,int l,int r,int x,int y,int rk){ if(!cnt)cnt=++tot; if(l==r){ anti_mp[l]=y;ans=rk+1; return; } int mid=(l+r)>>1; if(tr[cnt].la)pushdown(cnt,mid-l+1,r-mid); if(mid>=x)upd(ls,l,mid,x,y,rk); else upd(rs,mid+1,r,x,y,rk+mid-l+1-tr[ls].num); } int main(){ n=rd();m=rd(); gai(root,1,n+2*m,1,m); gai(root,1,n+2*m,n+m+1,n+m*2); h=m+1;t=n+m; for(int i=1;i<=m;++i){ int tag=rd();x=rd()-ans; if(tag==1){ y=rd()-ans; int pos; if(mp.find(x)!=mp.end())pos=mp[x]; else pos=x+m; mp[y]=pos; upd(root,1,n+2*m,pos,y,0); } else if(tag==2){ int pos; if(mp.find(x)!=mp.end())pos=mp[x]; else pos=x+m; if(anti_mp.find(pos)!=anti_mp.end())id=anti_mp[pos]; else id=x; add(root,1,n+2*m,pos,-1,0); add(root,1,n+2*m,--h,1,0); mp[x]=h; } else if(tag==3){ int pos; if(mp.find(x)!=mp.end())pos=mp[x]; else pos=x+m; if(anti_mp.find(pos)!=anti_mp.end())id=anti_mp[pos]; else id=x; add(root,1,n+2*m,pos,-1,0); add(root,1,n+2*m,++t,1,0); mp[x]=t; } else find(root,1,n+2*m,x); printf("%d\n",ans); } return 0; }

[SCOI2014]方伯伯的OJ(線段樹)