https://www.luogu.org/problemnew/show/P3960
树状数组做法:
定义一行中原来的元素为 初始时这一行前m-1m−1个元素中,没有离队过的元素。
我们观察到对于本来就在这一行中的元素,我们可以直接算出它的值,而不用存储。
那么我们判断每一次询问是不是在本行的原来的元素中,如果是,直接判断掉。
那么每一行的“非原来的元素”有多少个呢?
我们不知道一行会有多少个,但是我们知道,所有行的这样的元素个数的总和不超过q个。
这启发了我们对于每一行,只对其“非原来的元素”开一个树状数组维护。
而对于“原来的元素”,我们直接离线预处理。
预处理时需要对所有的询问按照x_i为第一关键字,询问编号为第二关键字排序。
再对最后一列单独处理。
线段数做法:
我们开1/2棵线段树,维护每个区间实际存在的数字个数,这样就可以利用区间长度-sum[x]的方法找到区间的第k个元素,同时我们维护1/2个vector,表示添加到末尾的数字,如果查找到的pos是在实际范围以内,那么直接按照pos计算,否则我们在vector中按照下标查找,之后再更新vector和线段树即可。
但会MLE
对每一行和最后一列维护n+1棵线段树和n+1个vector,由于动态开点我们的内存能够控制在nlogn的级别,vector中的元素在2q级别,不会超出内存限制,剩下的就按照刚才那样进行就行了,注意如果y=m,就不用对第x行的线段树进行操作了,否则我们就先在第x行找到答案,然后在第n+1棵线段树中找到第x个元素并添加到x中,然后把ans添加到n+1中。
#include
#define ll long long
using namespace std;
int n,m,q,x,y,pos,tot,lim,rt[300005],ls[12000005],rs[12000005],sm[12000005];vectorv[300005];
int query(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 query(ls[x],l,mid,v);
return query(rs[x],mid+1,r,v-tmp);
}
void update(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) update(ls[x],l,mid,p);
else update(rs[x],mid+1,r,p);
}
ll work1(int x,ll y)
{
pos=query(rt[n+1],1,lim,x);update(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 work2(int x,int y)
{
pos=query(rt[x],1,lim,y);update(rt[x],1,lim,pos);
ll ans=pos