1. 程式人生 > >NOIP2017day2 列隊(動態開點線段樹)

NOIP2017day2 列隊(動態開點線段樹)

題意

https://www.luogu.org/problemnew/show/P3960

思路

30 30 分暴力送溫暖,但其實正解就從這裡拓展出來。
最本質的想法無疑是優化純模擬,先分析操作會對列隊帶來的變化。首先出去上廁所的人從它這一行刪除,行末從最後一行補一個人。最後一行也對應的刪除那個人,並且加入上廁所的人,都是刪除並從後面加入。
顯然線段樹模擬刪除操作,加入的人用 vector

\text{vector} 存著,但是線段樹的空間貌似開不下,這個時候就需要動態開點了。對於每一次刪除操作,如果沒有這棵子樹,那就新增這棵子樹,每次最多開 log n \log_n
層,空間複雜度就壓縮到了 q log n q\log_n

程式碼

#include<bits/stdc++.h>
#pragma
GCC optimize(3)
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) typedef long long LL; using namespace std; const int N=3e5+3; const int NlogN=6e6+3; struct node{int lson,rson,sum;}; struct SegmentTree { node nd[NlogN]; int rt[N],tot; void build(){memset(rt,0,sizeof(rt));tot=0;} void create(int &k,int sum){nd[k=++tot]=(node){0,0,sum};} int query(int x,int y,int n){return query(rt[x],1,n,y);} int query(int &k,int l,int r,int K) { if(!k)create(k,r-l+1); nd[k].sum--; if(l==r)return l; int mid=l+r>>1,res; if(!nd[k].lson)res=mid-l+1; else res=nd[nd[k].lson].sum; if(K<=res)return query(nd[k].lson,l,mid,K); else return query(nd[k].rson,mid+1,r,K-res); } }ST; vector<LL>h[N]; int n,m,q; int main() { scanf("%d%d%d",&n,&m,&q); ST.build(); FOR(i,1,q) { int x,y,t;LL las,tmp; scanf("%d%d",&x,&y); t=ST.query(0,x,n+q); if(t<=n)las=1ll*t*m; else las=h[0][t-n-1]; if(y!=m) { h[x].push_back(las); t=ST.query(x,y,m+q); if(t<m)tmp=1ll*(x-1)*m+t; else tmp=h[x][t-m]; } else tmp=las; h[0].push_back(tmp); printf("%lld\n",tmp); } return 0; }