数据范围:\(r,c<=2000,m<=5000,1<=M_{ij},c\)(操做中的)\(,k<=10^9\)ios
形式化一下这个移动的过程,考虑若是没有修改,那么对于第\(x\)列的第\(i\)行,咱们能够预处理出它\(move\)一步以后会在第\(nxt(x)\)列哪一行,具体一点就是咱们能够对于每一列预处理出一个长度为\(r\)的数组,记为\(change[x][i]\),有了这个数组以后,咱们从\(change[1]\)合并到\(change[c]\)就能够获得一个新的数组\(change1\),其中\(change1[i]\)表示从第\(1\)列的第\(i\)行\(move\)到第\(c\)列以后在哪一行c++
记当前所在位置为\((nowx,nowy)\),考虑询问,由于列数\(c\)比较小,因此对于一次\(move\ k\)操做,咱们能够先暴力将当前的列跳到第\(1\)列(若是有那么屡次的话),这须要\(c-nowy+1\)步,而后剩下\(k-(c-nowy+1)\)步咱们能够先利用\(change1\)数组一圈一圈地跳(也就是\(c\)步\(c\)步跳),具体的话就是能够实现一个相似快速幂同样的东西,计算数组\(\{1,2,3,4,...,r\}\)在连续被\(change1\)做用了\(x\)次以后会变成什么(记为。。\(change1^{x}\)好了),而后跳完那么多圈以后仍是在第\(1\)列,因此这个时候的位置是\((change1^x[nowx'],nowy)\),这里的\(nowx'\)是暴力跳到第\(1\)列以后的所在行数,而后剩下\((k-(c-nowy+1))\%c\)步也是直接暴力跳就行了数组
如今考虑有修改,瓶颈在于要从新计算\(change1\),可是考虑一次修改对于\(change\)数组的影响,若是说修改的地方是\((x,y)\),那么只会影响到\(change[nxt(x)]\)这一个数组的值ui
因此咱们能够考虑将全部的\(change[i]\)丢进线段树做为线段树的底层节点维护的信息,这样\(change1\)就是线段树根节点处的值了,对于一次修改直接从新算一遍\(change[nxt(x)]\),而后将线段树中\(nxt(x)\)这个位置的值更新一下便可spa
mark:想清楚一次修改的影响code
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=2010; int a[N][N]; int n,m,q,nowx,nowy; struct Data{ int p[N]; void init(){for (int i=1;i<=n;++i) p[i]=i;} friend Data operator + (Data x,Data y){ for (int i=1;i<=n;++i) x.p[i]=y.p[x.p[i]]; return x; } }change[N]; namespace Seg{/*{{{*/ const int N=::N*4; int ch[N][2]; Data info[N]; int n,tot; void pushup(int x){info[x]=info[ch[x][0]]+info[ch[x][1]];} void _build(int x,int l,int r){ if (l==r){info[x]=change[l];return;} int mid=l+r>>1; ch[x][0]=++tot; _build(ch[x][0],l,mid); ch[x][1]=++tot; _build(ch[x][1],mid+1,r); pushup(x); } void build(int _n){n=_n; tot=1;_build(1,1,n);} void _update(int x,int d,int lx,int rx){ if (lx==rx){ info[x]=change[lx]; return; } int mid=lx+rx>>1; if (d<=mid) _update(ch[x][0],d,lx,mid); else _update(ch[x][1],d,mid+1,rx); pushup(x); } void update(int d){_update(1,d,1,n);} }/*}}}*/ int X(int x){x=(x+n)%n;return x==0?n:x;} int Y(int y){y=(y+m)%m;return y==0?m:y;} int work(int x,int y){ int nwy=Y(y+1),pre=X(x-1),nxt=X(x+1); int ret=x; if (a[pre][nwy]>a[ret][nwy]) ret=pre; if (a[nxt][nwy]>a[ret][nwy]) ret=nxt; return ret; } void prework(){ int pre,nxt,tmp,nwy; for (int x=1;x<=n;++x) for (int y=1;y<=m;++y) change[y].p[x]=work(x,y); Seg::build(m); } void modify(int y){ for (int i=1;i<=n;++i) change[y].p[i]=work(i,y); Seg::update(y); } void force_move(int &x,int &y,int tm){ for (int i=1;i<=tm;++i) x=change[y].p[x],y=Y(y+1); } Data ksm(Data &x,int y){ Data ret,base=x; ret.init(); for (;y;y>>=1,base=base+base) if (y&1) ret=ret+base; return ret; } void solve(int tm){ int tm1=min(tm,m-nowy+1); Data tmp; force_move(nowx,nowy,tm1); tmp=ksm(Seg::info[1],(tm-tm1)/m); nowx=tmp.p[nowx]; force_move(nowx,nowy,(tm-tm1)%m); printf("%d %d\n",nowx,nowy); } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif char op[10]; int x,y,z; scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) scanf("%d",&a[i][j]); prework(); nowx=1; nowy=1; scanf("%d",&q); for (int i=1;i<=q;++i){ scanf("%s",op); if (op[0]=='m'){ scanf("%d",&x); solve(x); } else{ scanf("%d%d%d",&x,&y,&z); a[x][y]=z; modify(Y(y-1)); } } }