题解 P3960 【列队】

Awar3ness

2018-08-11 12:11:45

Solution

## 前言 noip2017的时候我连线段树都不会,树状数组也只会板子,而且当时因为肛T2时间太多连50分都没写,30分就跑路了。现在回来看这个题,真的是一言难尽啊。 ## 算法1 对于前50%的数据,我们取出所有询问用到的行和最后一列进行暴力模拟即可。 期望得分:50 ## 算法2 对于11-16测试点,我们开1/2棵线段树,维护每个区间实际存在的数字个数,这样就可以利用区间长度-sum[x]的方法找到区间的第k个元素,同时我们维护1/2个vector,表示添加到末尾的数字,如果查找到的pos是在实际范围以内,那么直接按照pos计算,否则我们在vector中按照下标查找,之后再更新vector和线段树即可。 注意:线段树维护的是1~n+q/m+q的区间,所以空间记得开大一倍。 ## 算法3 对每一行和最后一列维护n+1棵线段树和n+1个vector,由于动态开点我们的内存能够控制在$nlogn$的级别,vector中的元素在$2q$级别,不会超出内存限制,剩下的就按照算法2进行就行了,注意如果$y=m$,就不用对第x行的线段树进行操作了,否则我们就先在第x行找到答案,然后在第n+1棵线段树中找到第x个元素并添加到x中,然后把ans添加到n+1中。 ## 算法3代码 其实这道题代码还是很好写的,35行,不到1K的长度。 ```cpp #include <bits/stdc++.h> #define ll long long using namespace std; int n,m,q,x,y,pos,tot,lim,rt[300005],ls[12000005],rs[12000005],sm[12000005];vector<ll>v[300005]; int que(int x,int l,int r,int v) { if(l==r) return l; int mid=(l+r)>>1,tmp=mid-l+1-sm[ls[x]]; if(v<=tmp) return que(ls[x],l,mid,v); return que(rs[x],mid+1,r,v-tmp); } void upd(int &x,int l,int r,int p) { if(!x) x=++tot;sm[x]++; if(l==r) return;int mid=(l+r)>>1; if(p<=mid) upd(ls[x],l,mid,p); else upd(rs[x],mid+1,r,p); } ll wk1(int x,ll y) { pos=que(rt[n+1],1,lim,x);upd(rt[n+1],1,lim,pos); ll ans=pos<=n?1ll*pos*m:v[n+1][pos-n-1]; return v[n+1].push_back(y?y:ans),ans; } ll wk2(int x,int y) { pos=que(rt[x],1,lim,y);upd(rt[x],1,lim,pos); ll ans=pos<m?1ll*(x-1)*m+pos:v[x][pos-m]; return v[x].push_back(wk1(x,ans)),ans; } int main() { scanf("%d%d%d",&n,&m,&q);lim=max(n,m)+q; for(;q--;printf("%lld\n",y==m?wk1(x,0):wk2(x,y))) scanf("%d%d",&x,&y); } ```