[线段树][快速幂][循环节] Jzoj P6278 跳房子
题解
- 我们可以考虑用线段树来做,我们记录对于每一列,在每一行其向下一列会走到哪
- 这样的话每一列都建立了一个置换,之后我们只要把他们全部合并起来
- 在行走的时候,如果步数够得话就直接走循环节,否则一步一步走就可以
- 修改操作的话,我们只用修改当前点的左上、左和左下就行了,它只对这些点有贡献
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #define N 2010 5 using namespace std; 6 int n,m,Q,px,py,a[N][N]; 7 char s[10]; 8 struct node 9 { 10 int p[N]; 11 node () { for (int i=1;i<=n;i++) p[i]=i; } 12 node operator * (const node &b) const { node a; for (int i=1;i<=n;i++) a.p[i]=b.p[p[i]]; return a; } 13 }w[N],t[N*4]; 14 node ksm(node a,int b) { node r; for (;b;b>>=1,a=a*a) if (b&1) r=r*a; return r; } 15 int pos(int x,bool y) { return (x==(y?m+1:n+1))?1:(!x?(y?m:n):x); } 16 void change(int x,int y) 17 { 18 int r=0; x=pos(x,0),y=pos(y,1); 19 for (int i=-1;i<=1;i++) 20 { 21 int xx=pos(i+x,0),yy=pos(1+y,1); 22 if (r<a[xx][yy]) r=a[xx][yy],w[y].p[x]=xx; 23 } 24 } 25 void build(int d,int l,int r) 26 { 27 if (l==r) { t[d]=w[l]; return; } 28 int mid=l+r>>1; 29 build(d*2,l,mid),build(d*2+1,mid+1,r),t[d]=t[d*2]*t[d*2+1]; 30 } 31 void modify(int d,int l,int r,int x) 32 { 33 if (l==r) { t[d]=w[x]; return; } 34 int mid=l+r>>1; 35 if (x<=mid) modify(d*2,l,mid,x); else modify(d*2+1,mid+1,r,x); 36 t[d]=t[d*2]*t[d*2+1]; 37 } 38 void move(int &x,int &y,int k){ while (k--) x=w[y].p[x],y=pos(y+1,1); } 39 int main() 40 { 41 freopen("jump.in","r",stdin),freopen("jump.out","w",stdout),scanf("%d%d",&n,&m); 42 for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]); 43 for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) change(i,j); 44 build(1,1,m),scanf("%d",&Q),px=py=1; 45 for (int x,y,z,len;Q;Q--) 46 { 47 scanf("%s",s); 48 if (s[0]=='m') 49 { 50 scanf("%d",&x),len=min(x,m-py+1),move(px,py,len),x-=len; 51 if (x) px=ksm(t[1],x/m).p[px],x%=m,move(px,py,x); 52 printf("%d %d\n",px,py); 53 } 54 else scanf("%d%d%d",&x,&y,&z),a[x][y]=z,change(x-1,y-1),change(x,y-1),change(x+1,y-1),modify(1,1,m,pos(y-1,1)); 55 } 56 }