1. 程式人生 > >[NOIP2017]列隊(線段樹)

[NOIP2017]列隊(線段樹)

考慮n=1的做法,就是支援:

1.線上刪一個數

2.在結尾加一個數

3.查詢序列的第y個數

用線段樹記錄區間內被刪元素的個數,可以通過線段樹上二分快速得解,對於新增的數,用vector記錄即可。

對於滿分同樣如此,對每行開一個線段樹,再對最後一列單獨開一個。

對於每次操作:

若在最後一列:就是對最後一列直接使用n=1的做法。

若不在:對第x列的前m-1個用n=1的做法,再將最後一列的第x個加入第x列的末尾,然後再對最後一列使用n=1的做法。

 1 #include<cstdio>
 2 #include<vector>
 3 #include<algorithm>
 4
#define lson ls[x],L,mid 5 #define rson rs[x],mid+1,R 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=300010,M=N*40; 11 ll ans; 12 int n,m,Q,mx,x,y,nd,rt[N],sm[M],ls[M],rs[M]; 13 vector<ll>V[N]; 14 15 void ins(int
&x,int L,int R,int pos){ 16 if (!x) x=++nd; 17 if (L==R){ sm[x]++; return; } 18 int mid=(L+R)>>1; 19 if (pos<=mid) ins(lson,pos); else ins(rson,pos); 20 sm[x]=sm[ls[x]]+sm[rs[x]]; 21 } 22 23 int que(int x,int L,int R,int pos){ 24 if (L==R) return L; 25
int mid=(L+R)>>1,t=mid-L+1-sm[ls[x]]; 26 if (t>=pos) return que(lson,pos); else return que(rson,pos-t); 27 } 28 29 int main(){ 30 scanf("%d%d%d",&n,&m,&Q); mx=max(n,m)+Q; 31 rep(i,0,n) V[i].push_back(0); 32 while (Q--){ 33 scanf("%d %d",&x,&y); 34 if (y==m){ 35 int s=que(rt[0],1,mx,x); 36 if (s<=n) ans=1ll*s*m; else ans=V[0][s-n]; 37 printf("%lld\n",ans); ins(rt[0],1,mx,s); V[0].push_back(ans); 38 continue; 39 } 40 int t=que(rt[x],1,mx,y); 41 if (t<m) ans=1ll*(x-1)*m+t; else ans=V[x][t-(m-1)]; 42 printf("%lld\n",ans); ins(rt[x],1,mx,t); 43 int s=que(rt[0],1,mx,x); ll tmp=ans; 44 if (s<=n) ans=1ll*s*m; else ans=V[0][s-n]; 45 V[x].push_back(ans); ins(rt[0],1,mx,s); V[0].push_back(tmp); 46 } 47 return 0; 48 }