题目:https://www.luogu.org/problemnew/show/P3960
NOIP 题,不用很复杂的数据结构...但又参考了许多;
要求支持维护删除第 k 个和在末尾插入的数据结构,线段树就很好;
所以每行一个线段树维护前 m-1 个元素,最后一列一个线段树即可;
但 n+1 个线段树显然空间太大,考虑利用一开始是按顺序排列的特点;
也就是不用实际开出来所有线段树的点,只要它是满的就可以直接算出来;
对于新加入线段树的点,如果真的加入线段树里,又不会算空间了...
但是新加入的点只有 2*q 个,可以给每行和最后一列开一个 vector 记录新加入的点,查询时如果不是原装点,就去 vector 里提取;
也不需要去 vector 里删除元素,在对应线段树上动态开出这个点的位置表示一下就行了,将来查询的时候就不会算这个元素;
把基础数据结构组合起来,得到简洁的做法...希望下次能自己想出来。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define mid ((l+r)>>1) using namespace std; typedef long long ll; int const maxn=3e5+5,maxm=12000000; int n,m,q,rt[maxn],cnt,sum[maxm],ls[maxm],rs[maxm],mx; vector<ll>v[maxn];//ll int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return ret*f; } int query(int x,int l,int r,int k) { if(l==r)return l; int num=(mid-l+1)-sum[ls[x]]; if(num>=k)return query(ls[x],l,mid,k); return query(rs[x],mid+1,r,k-num); } void update(int &x,int l,int r,int pos) { if(!x)x=++cnt; sum[x]++;//sum 是删除的数量 if(l==r)return;// if(pos<=mid)update(ls[x],l,mid,pos); else update(rs[x],mid+1,r,pos); } ll del(int x,int y) { int pos=query(rt[x],1,mx,y); update(rt[x],1,mx,pos); if(pos<m)return (ll)(x-1)*m+pos; return v[x][pos-m]; } ll del2(int x) { int pos=query(rt[n+1],1,mx,x); update(rt[n+1],1,mx,pos); if(pos<=n)return (ll)pos*m; return v[n+1][pos-n-1]; } int main() { n=rd(); m=rd(); q=rd(); mx=max(n,m)+q; for(int i=1,x,y;i<=q;i++) { x=rd(); y=rd(); if(y==m) { ll tmp=del2(x); printf("%lld ",tmp); v[n+1].push_back(tmp); } else { ll tmp=del(x,y); printf("%lld ",tmp); v[n+1].push_back(tmp); ll tmp2=del2(x); v[x].push_back(tmp2); } } return 0; }