1. 程式人生 > >NOIP2017 列隊 線段樹(指標版)+vector

NOIP2017 列隊 線段樹(指標版)+vector

題目描述
題目

考試有想過每行建一棵樹,但是發現空間會爆,就去打模擬了。。。
然而,正解就是每行建一棵樹(最後一列單獨建一棵),只不過是把插入操作改成刪除操作就好了。
因為每次離隊影響到的只會是人所在的行和列(如果是最後一列的離隊只會影響行)。
對於後面插進來的元素用vector存一下就好

懶得去算要開多大的空間,並且最近玩資料結構玩得有點瘋,就打了個指標版的線段樹(並沒有和打平衡樹一樣查錯查半天,有點開心)

下面是程式碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm> #include<vector> #define ll long long using namespace std; const int N=300010; ll n,m,q,mn; struct node { node* ch[2];ll s; node(){ch[0]=ch[1]=NULL;s=1;} void maintain() { s=0; if(ch[0] != NULL) s+=ch[0]->s; if(ch[1] != NULL) s+=ch[1
]->s; } }; node* rt[N]; vector<ll> g[N]; ll query(node* o,ll l,ll r,ll x) { if(o == NULL) return l+x-1; if(l == r) return l; int mid=(l+r)>>1,ss=(o->ch[0]==NULL?0:o->ch[0]->s); int h=mid-l+1-ss; if(h >= x) return query(o->ch[0],l,mid,x); return
query(o->ch[1],mid+1,r,x-h); } void del(node* &o,ll l,ll r,ll x) { if(o == NULL) o=new node(); if(l == r) return ; int mid=(l+r)>>1; if(x <= mid) del(o->ch[0],l,mid,x); else del(o->ch[1],mid+1,r,x); o->maintain(); } ll del_row(ll x) { ll idx=query(rt[n+1],1,mn,x); del(rt[n+1],1,mn,idx); if(idx > n) return g[n+1][idx-n-1]; return idx*m; } ll del_line(ll x,ll y) { ll idx=query(rt[x],1,mn,y); del(rt[x],1,mn,idx); if(idx >= m) return g[x][idx-m]; return (x-1)*m+idx; } int read() { int out=0,f=1;char c=getchar(); while(c < '0' || c > '9') {if(c == '-') f=-1;c=getchar();} while(c >= '0' && c <= '9' ) {out=(out<<1)+(out<<3)+c-'0';c=getchar();} return out*f; } void solve() { n=read();m=read();q=read();mn=max(n,m)+q; for(int i=1;i<=n+1;i++) rt[i]=NULL; for(int i=1;i<=q;i++) { ll x=read(),y=read(); if(y == m) { ll id=del_row(x); g[n+1].push_back(id); printf("%lld\n",id); } else { ll id1=del_line(x,y),id2=del_row(x); g[x].push_back(id2);g[n+1].push_back(id1); printf("%lld\n",id1); } } } int main() { solve(); return 0; }