noip出这个题简直丧心病狂,省选都没考过这么恶心的splay
这个题思路不是很难找,要点只有两个:特殊数据和动态结构
首先纵向发生变化的一定是最右边的一列,所以它是特殊的
然后询问只有1e5 ,而矩阵是1e5*1e5的,所以询问少,是特殊的
所有的操作等价于从一行中选择一个,插入到最后一列的队尾,从最后一列中选择一个,插入到一列的队尾
一开始想用离线线段树做,存sz,合并不会被询问的点团,但这样不被询问的点团也有可能被询问,所以还是需要动态维护
所以就需要动态的结构,用splay维护最右边一列、每一行
利用splay的sz和起始值大小来压缩区间,需要时分裂、删除和插入
非常难写,写了一天
码:
#include<iostream>
#include<cstdio>
using namespace std;
#define N 3000005
int ch[N][2],op,fu[N],rt[N],tot,q;
long long sz[N],szh[N],v[N],c,a,b,i,x,y,n,m;
void up(int o)
{
szh[o]=szh[ch[o][0]]+szh[ch[o][1]]+sz[o];
}
void set(int a,int wh,int child)
{
ch[a][wh]=child;
fu[child]=a;
up(a);
}
int getwh(int o)
{return ch[fu[o]][0]==o?0:1;
}
void rotate(int o)
{
int fa=fu[o],ye=fu[fa];
int wh=getwh(o);
set(fa,wh,ch[o][wh^1]);
set(o,wh^1,fa);
fu[o]=ye;
if(ye!=0)
{
ch[ye][ch[ye][0]==fa?0:1]=o;
}
}
void splay(int hang,int o,int tar)
{
for(;fu[o]!=tar;rotate(o))
if(fu[fu[o]]!=tar)
getwh(o)==getwh(fu[o])?rotate(fu[o]):rotate(o);
if(tar==0)rt[hang]=o;
}
int xin()
{
v[++tot]=a;
sz[tot]=szh[tot]=b;
return tot;
}
void del(int hang,int o)
{
splay(hang,o,0);
if(ch[o][0]==0)
{
fu[ch[o][1]]=0;
rt[hang]=ch[o][1];
}else if(ch[o][1]==0)
{
fu[ch[o][0]]=0;
rt[hang]=ch[o][0];
}else
{
int p=ch[o][0];
while(ch[p][1])p=ch[p][1];
ch[p][1]=ch[o][1];
fu[ch[o][1]]=p;
fu[ch[o][0]]=0;
rt[hang]=ch[o][0];splay(hang,p,0);
}
}
void zhao(int qu,int bu,int mb)
{
int o=rt[qu],last=0;
while(o)
{
last=o;
if(szh[ch[o][0]]+1<=mb&&szh[ch[o][0]]+sz[o]>=mb)
{
mb-=szh[ch[o][0]];
c=v[o]+mb-1;
splay(qu,o,0);
if(op==0)//取出行里,补到列里
{
if(sz[o]==1)//直接删
{
del(qu,o);
}else
if(mb==1)//只裂出右边
{
a=c+1; b=sz[o]-1;
int rr=xin();
if(ch[o][1]==0)
{
set(o,1,rr);
}else
{
int p=ch[o][1];
while(ch[p][0])p=ch[p][0];
set(p,0,rr);
}
splay(qu,rr,0);
del(qu,o);
}
else if(mb==sz[o])//只裂出左边
{
a=v[o]; b=sz[o]-1;
int ll=xin();
if(ch[o][0]==0)
{
set(o,0,ll);
}else
{
int p=ch[o][0];
while(ch[p][1])p=ch[p][1];
set(p,1,ll);
}
splay(qu,ll,0);
del(qu,o);
}else
{
a=c+1; b=sz[o]-mb;
int rr=xin();
if(ch[o][1]==0)
{
set(o,1,rr);
}else
{
int p=ch[o][1];
while(ch[p][0])p=ch[p][0];
set(p,0,rr);
}
splay(qu,rr,0);
splay(qu,o,0);
a=v[o]; b=mb-1;
int ll=xin();
if(ch[o][0]==0)
{
set(o,0,ll);
}else
{
int p=ch[o][0];
while(ch[p][1])p=ch[p][1];
set(p,1,ll);
}
splay(qu,ll,0);
del(qu,o);
}
o=rt[bu];
while(ch[o][1])o=ch[o][1];
a=c,b=1;
int rr=xin();
if(o)set(o,1,rr);
else rt[bu]=rr;
splay(bu,rr,0);
}else if(op==1)//取出列里补到行里
{
c=v[o];
del(qu,o);
o=rt[bu];
while(ch[o][1])o=ch[o][1];
a=c,b=1;
int rr=xin();
if(o)set(o,1,rr);else rt[bu]=rr;
splay(bu,rr,0);
}else//取出列里补到列里
{
c=v[o];
del(qu,o);
o=rt[bu];
while(ch[o][1])o=ch[o][1];
a=c,b=1;
int rr=xin();
if(o)set(o,1,rr);else rt[bu]=rr;
splay(bu,rr,0);
}
return;
}
if(szh[ch[o][0]]>=mb)o=ch[o][0];
else mb-=sz[o]+szh[ch[o][0]],o=ch[o][1];
}
}
int jian(int l,int r)
{
if(l>r)return 0;
int mid=(l+r)>>1;
sz[++tot]=1;
szh[tot]=1;
v[tot]=m*mid;
int lin=tot;
ch[lin][0]=jian(l,mid-1);
if(ch[lin][0]) fu[ch[lin][0]]=lin;
ch[lin][1]=jian(mid+1,r);
if(ch[lin][1]) fu[ch[lin][1]]=lin;
szh[lin]+=szh[ch[lin][0]]+szh[ch[lin][1]];
return lin;
}
int main()
{
scanf("%lld%lld%d",&n,&m,&q);
for(i=1;i<=n;i++)
{
a=(i-1)*m+1;
b=m-1;
rt[i]=xin();
}
rt[0]=tot+1;
jian(1,n);
for(i=1;i<=q;i++)
{
scanf("%d%d",&x,&y);
if(y==m)
{
op=2;
zhao(0,0,x);
printf("%lld\n",c);
}else
{
op=0;
zhao(x,0,y); printf("%lld\n",c);
op=1;
zhao(0,x,x);
}
}
}