12062
前言
出题人题解。
其实是军训的时候整出来的 idea……
这题原本可能是今年的省选题,不过一月的时候因为一些原因我主动撤下了,于是塞进了 THUPC 中。(快说:谢谢 Itst)
命题会上大家发现这似乎是唯一一道能用的 ds 了,所以就毫无疑问地选上了。
实际赛时居然没人写正解,全去写根号分治然后卡常了……
我寻思着这题开了
本题应该会有 Ynoi ver.,只保留做法中的第二种情况并开大数据范围卡常。
思路
首先注意到一个小结论:最多排序两次。
为什么?观察到如果矩阵只有
对于矩阵中的每个数,我们考虑把比其小的赋值为
由于第二次就退出和第二次排序后第三次退出对矩阵形态的实践行为是一样的,我们实际上只用分为两种情况:
- 一次也没排序。
- 恰好排序了两次。
至于是哪种情况,我们只用维护每行是否分别有序就行。也就是我们只用维护每个行内相邻的元素是否均有序。
这个是容易做到 set 也是
分辨出是哪种情况后,由于第一种情况就是输出矩阵对应位置元素,直接维护矩阵形态即可,显然是
于是接下来我们只用考虑第二种情况。这种情况实际上是在求:假设各行分别取出第
一种很基础的思路是根号分治,场上确实大家都是这么写的。但是这个做法非常难写,所以我们这里换个思路。
注意到本题始终保持了排列的性质,于是考虑反过来维护每个数在矩阵中对应的行号。那么问题就变成了找到最小的位置,使得这个这个位置之前出现了至少
这个显然是可以直接分块维护的。具体地,我们设
信息维护与查询较为简单,不作赘述了。总之取
值得一提的是,如果允许离线,就算不保证是排列,该做法也仍旧适用。
Code
// ぼたぼたと
// しゃりしゃりと
// うとうとと
// はらはらと
// ぱりぱりと
// しんしんと
// この世界を閉ざした
// 今もひとり宇宙を覗き込んだまま
#include <bits/stdc++.h>
using uint = unsigned;
const uint Lim=200000,B=500;
uint n,m,q,cnt;
uint A[Lim+5],P[Lim+5],L[Lim+5],bid;
uint Cnt[Lim/B+5][Lim+5],CCnt[Lim/B+5][Lim+5];
int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
freopen("QAQ.out","w",stdout);
#endif
scanf("%u%u%u",&n,&m,&q);
for(uint i=0;i<n;i++)for(uint j=0;j<m;j++)scanf("%u",A+i*m+j),P[--A[i*m+j]]=i*m+j,cnt+=j&&A[i*m+j-1]>A[i*m+j];
for(uint i=0;i<n*m;i++)L[i]=P[i]/m;
for(uint i=0;i<n*m;i++)
{
if(!(i%B))
{
for(uint j=0;j<n;j++)Cnt[bid+1][j]=Cnt[bid][j];
for(uint j=0;j<m;j++)CCnt[bid+1][j]=CCnt[bid][j];
bid++;
}
CCnt[bid][Cnt[bid][L[i]]++]++;
}
while(q--)
{
uint o;scanf("%u",&o);
if(o==1)
{
uint x1,y1,x2,y2;scanf("%u%u%u%u",&x1,&y1,&x2,&y2),x1--,y1--,x2--,y2--;
if(x1==x2&&y1==y2)continue;
if(y1>y2)std::swap(y1,y2),std::swap(x1,x2);
if(y1)cnt-=A[x1*m+y1-1]>A[x1*m+y1];
if(y1<m-1)cnt-=A[x1*m+y1+1]<A[x1*m+y1];
if(y2&&(x1!=x2||y1+1!=y2))cnt-=A[x2*m+y2-1]>A[x2*m+y2];
if(y2<m-1)cnt-=A[x2*m+y2+1]<A[x2*m+y2];
uint a=A[x1*m+y1],b=A[x2*m+y2];std::swap(P[a],P[b]),std::swap(L[a],L[b]),std::swap(A[P[a]],A[P[b]]);
if(y1)cnt+=A[x1*m+y1-1]>A[x1*m+y1];
if(y1<m-1)cnt+=A[x1*m+y1+1]<A[x1*m+y1];
if(y2&&(x1!=x2||y1+1!=y2))cnt+=A[x2*m+y2-1]>A[x2*m+y2];
if(y2<m-1)cnt+=A[x2*m+y2+1]<A[x2*m+y2];
if(L[a]==L[b])continue;
if(a>b)std::swap(a,b);
for(uint j=a/B+1;j<=b/B;j++)--CCnt[j][--Cnt[j][L[b]]],CCnt[j][Cnt[j][L[a]]++]++;
}
else
{
uint x,y;scanf("%u%u",&x,&y),x--,y--;if(!cnt){printf("%u\n",A[x*m+y]+1);continue;}
uint p=0;while(CCnt[p+1][y]<=x)p++;
uint t=CCnt[p][y];
p*=B;
while(t<=x)
{
if(Cnt[p/B][L[p]]++==y)t++;
p++;
}
printf("%u\n",p);
do p--,Cnt[p/B][L[p]]--;while(p%B);
}
}
return 0;
}