前置技能:動態開點線段樹html
題意:node
Sylvia
是一個熱愛學習的女♂孩子。ios前段時間,
Sylvia
參加了學校的軍訓。衆所周知,軍訓的時候須要站方陣。gitSylvia 所在的方陣中有 n*m 名學生,方陣的行數爲 n ,列數爲 m 。數組
爲了便於管理,教官在訓練開始時,按照從前到後,從左到右的順序給方陣中 的學生從 1 到 n*m 編上了號碼(參見後面的樣例)。即:初始時,第 i 行第 j 列 的學生的編號是 (i−1)*m+j 。學習
然而在練習方陣的時候,常常會有學生由於各類各樣的事情須要離隊。在一天 中,一共發生了 qq 件這樣的離隊事件。每一次離隊事件能夠用數對 (x,y) (1≤x≤n, 1≤y≤m)(x,y)(1≤x≤n,1≤y≤m) 描述,表示第 x 行第 y 列的學生離隊。ui
在有學生離隊後,隊伍中出現了一個空位。爲了隊伍的整齊,教官會依次下達 這樣的兩條指令:spa
向左看齊。這時第一列保持不動,全部學生向左填補空缺。不難發如今這條 指令以後,空位在第 x 行第 m 列。code
向前看齊。這時第一行保持不動,全部學生向前填補空缺。不難發如今這條 指令以後,空位在第 n 行第 m 列。htm
教官規定不能有兩個或更多學生同時離隊。即在前一個離隊的學生歸隊以後, 下一個學生才能離隊。所以在每個離隊的學生要歸隊時,隊伍中有且僅有第 n 行 第 m 列一個空位,這時這個學生會天然地填補到這個位置。
由於站方陣真的很無聊,因此
Sylvia
想要計算每一次離隊事件中,離隊的同窗 的編號是多少。注意:每個同窗的編號不會隨着離隊事件的發生而改變,在發生離隊事件後 方陣中同窗的編號多是亂序的。
q<=500 50分 模擬
只有500組詢問,妥妥的暴力
可是若是開出n*m的數組,空間就會炸
不難發現每一個人的出隊只會影響當前行和最後一列
因此只要維護這p行和最後一列的信息,空間複雜度:O(p*m+n)
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 501 #define M 50001 typedef long long LL; LL pos[N][M],last[M]; struct node { int x,y; }e[N]; int h[N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int main() { int n,m,q; read(n); read(m); read(q); for(int i=1;i<=q;++i) { read(e[i].x); read(e[i].y); h[i]=e[i].x; } sort(h+1,h+q+1); int tot=unique(h+1,h+q+1)-h-1; LL t; for(int i=1;i<=tot;++i) { t=(LL)(h[i]-1)*m; for(int j=1;j<=m;++j) pos[i][j]=++t; } for(int i=1;i<=n;++i) last[i]=last[i-1]+m; int nx; LL ans; for(int i=1;i<=q;++i) { nx=lower_bound(h+1,h+tot+1,e[i].x)-h; if(e[i].y==m) ans=last[h[nx]]; else ans=pos[nx][e[i].y]; cout<<ans<<'\n'; if(e[i].y!=m) { for(int j=e[i].y;j<m-1;++j) pos[nx][j]=pos[nx][j+1]; pos[nx][m-1]=last[h[nx]]; } for(int j=h[nx];j<n;++j) last[j]=last[j+1]; last[n]=ans; } }
x=1
所有操做只涉及第一行和最後一列
能夠開兩個數組維護第一行和最後一列
整個過程能夠總結爲兩個操做
1.從序列中找出第k個數並刪除
2.把刪除的數叫入到另外一個序列
咱們能夠開兩棵線段樹維護這些信息
#include<cstdio> #include<vector> #include<iostream> #include<algorithm> using namespace std; #define N 300001 typedef long long LL; int n; int sum[N<<2]; LL a[N<<1]; int sum2[N<<3]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void build(int k,int l,int r) { sum[k]=r-l+1; if(l==r) return; int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } int query(int k,int l,int r,int pos) { if(l==r) return l; int mid=l+r>>1; if(pos<=sum[k<<1]) return query(k<<1,l,mid,pos); return query(k<<1|1,mid+1,r,pos-sum[k<<1]); } void change(int k,int l,int r,int pos) { if(l==r) { sum[k]=0; return; } int mid=l+r>>1; if(pos<=mid) change(k<<1,l,mid,pos); else change(k<<1|1,mid+1,r,pos); sum[k]=sum[k<<1]+sum[k<<1|1]; } void build2(int k,int l,int r) { if(l==r) { if(l<=n) sum2[k]=1; return; } int mid=l+r>>1; build2(k<<1,l,mid); build2(k<<1|1,mid+1,r); sum2[k]=sum2[k<<1]+sum2[k<<1|1]; } int query2(int k,int l,int r,int pos) { if(l==r) return l; int mid=l+r>>1; if(pos<=sum2[k<<1]) return query2(k<<1,l,mid,pos); return query2(k<<1|1,mid+1,r,pos-sum2[k<<1]); } void change2(int k,int l,int r,int pos,int w) { if(l==r) { sum2[k]=w; return; } int mid=l+r>>1; if(pos<=mid) change2(k<<1,l,mid,pos,w); else change2(k<<1|1,mid+1,r,pos,w); sum2[k]=sum2[k<<1]+sum2[k<<1|1]; } int main() { int m,q; read(n); read(m); read(q); build(1,1,m-1); int i=1; LL j=m; for(;i<=n;j+=m,++i) a[i]=j; build2(1,1,n+q); int x,y; LL ans; for(int i=1;i<=q;++i) { read(x); read(y); if(y<=sum[1]) { ans=query(1,1,m-1,y); cout<<ans<<'\n'; change(1,1,m-1,ans); change2(1,1,n+q,n+i,1); a[n+i]=ans; } else { y-=sum[1]; y=query2(1,1,n+q,y); cout<<a[y]<<'\n'; change2(1,1,n+q,y,0); change2(1,1,n+q,n+i,1); a[n+i]=a[y]; } } }
100分
100分和80分其實很類似了,思想上沒有太大的變化
想到不少位置都沒有動,因此用動態開點線段樹
另外一個不一樣點是位置數組須要動態維護,因此開個vector存便可
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #define RG register #define il inline #define iter iterator #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) using namespace std; typedef long long ll; const int N=300005,M=6000100; vector<ll>S[N]; int n,m,Q,tot,root[N],ls[M],rs[M],v[M],cnt=0; inline void Modify(int &rt,int l,int r,int sa){ if(!rt)rt=++cnt; v[rt]++; if(l==r)return ; int mid=(l+r)>>1; if(sa<=mid)Modify(ls[rt],l,mid,sa); else Modify(rs[rt],mid+1,r,sa); } inline int qry(int rt,int l,int r,int k){ if(l==r)return l; int mid=(l+r)>>1; int sum=mid-l+1-v[ls[rt]]; if(k<=sum)return qry(ls[rt],l,mid,k); return qry(rs[rt],mid+1,r,k-sum); } inline ll caline(int x){ int r=qry(root[n+1],1,tot,x); Modify(root[n+1],1,tot,r); return r<=n?1ll*(r-1)*m+m:S[n+1][r-n-1]; } inline ll calrow(int x,int y){ int r=qry(root[x],1,tot,y); Modify(root[x],1,tot,r); return r<m?1ll*(x-1)*m+r:S[x][r-m]; } void work() { int x,y; ll ret,tmp; scanf("%d%d%d",&n,&m,&Q); tot=Max(n,m)+Q; while(Q--){ scanf("%d%d",&x,&y); if(y==m){ ret=caline(x); S[n+1].push_back(ret); printf("%lld\n",ret); } else{ ret=calrow(x,y); printf("%lld\n",ret); S[n+1].push_back(ret); tmp=caline(x); S[x].push_back(tmp); } } } int main() { freopen("pp.in","r",stdin); freopen("pp.out","w",stdout); work(); return 0; }