Link
我们知道每走一步一定会往前走一列。
考虑求出(next_i)表示从((i,1))开始走,下一次走到第一列的时候在第几行。
可以先预处理(a_{i,j}=[l,r])表示(forall kin[l,r]),从((k,1))开始走,会走到((i,j)),不难发现这一定是个区间。
(O(rc))地预处理(a)后,就可以(O(r))地计算(next)了。
对于修改((x,y,v)),第(y)列中(a)发生变动的点的一定在([x-2,x+2])行。
那么我们可以对于这(5)个点,暴力往后修改(a),单次复杂度为(O(c))。
对于询问,考虑(x
ightarrow next_x)会构成一棵内向基环树。
我们先计算完整地走完了多少次所有列,再计算这样会在基环树上到达哪个点,然后再暴力往后走即可,单次复杂度为(O(r+c))。
总的时间复杂度为(O(rc+q(r+c)))。
#include<cstdio>
#include<cctype>
#include<cstring>
#include<utility>
#define fi first
#define se second
using pi=std::pair<int,int>;
const int N=2007;
int r,c,next[N],val[N][N],num[N];pi a[N][N];
char get(){char c=getchar();while(!islower(c))c=getchar();return c;}
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int pre(int x){return x==1? r:x-1;}
pi merge(pi p,pi q){return ~p.fi? (~q.fi? (p.se%r+1==q.fi? pi{p.fi,q.se}:pi{q.fi,p.se}):p):q;}
int go(int x,int y)
{
y=y%c+1;int mx=0,id=0;
for(int i=0,p=pre(x);i<3;++i,p=p%r+1) if(mx<val[p][y]) mx=val[id=p][y];
return id;
}
void cal(int x,int y)
{
if(y==1) return a[x][y]={x,x},void();
a[x][y]={-1,-1};
for(int i=0,p=pre(x);i<3;++i,p=p%r+1) if(go(p,y-1)==x) a[x][y]=merge(a[x][y],a[p][y-1]);
}
void build()
{
for(int i=1,t;i<=r;++i)
{
if(!~a[i][c].fi) continue;
next[a[i][c].fi]=t=go(i,c);
for(int j=a[i][c].fi%r+1;j!=a[i][c].se%r+1;j=j%r+1) next[j]=t;
}
}
void rebuild(int x,int y){for(;y^1;x=go(x,y),y=y%c+1)cal(x,y);}
int main()
{
r=read(),c=read();
for(int i=1;i<=r;++i) for(int j=1;j<=c;++j) val[i][j]=read();
for(int j=1;j<=c;++j) for(int i=1;i<=r;++i) cal(i,j);
build();
for(int Q=read(),x=1,y=1;Q;--Q)
if(get()=='m')
{
int k=read(),now=1;memset(num,0,sizeof num);
while(k&&y^1) x=go(x,y),y=y%c+1,--k;
while(k>=c&&!num[x]) num[x]=now++,x=next[x],k-=c;
for(k%=(now-num[x])*c;k>=c;) k-=c,x=next[x];
while(k--) x=go(x,y++);
printf("%d %d
",x,y);
}
else
{
int x=read(),y=read(),v=read();val[x][y]=v;
for(int i=0,p=pre(pre(x));i<5;++i,p=p%r+1) rebuild(p,y);
build();
}
}