「CERC2017」Donut Drone
阅读原文时间:2023年07月11日阅读:2

题目链接

洛谷P4739

题目翻译:

你正在模拟无人机探索一个不稳定的环状行星的过程。技术上说,无人机正在穿过一个环形网格———一个在两维上都首尾环绕在一起的矩形网格。格子的行号从上到下依次编号为\(1\)到\(r\),列号

从上到下依次编号为\(1\)到\(c\)。每个格子还有一个海拔——这是个正数。

无人机一开始位于第一行第一列的格子。每一步,无人机会考虑这样三个格子:右边、右上方、右下方(注意这个网格首尾相接)。无人机会飞到它们之中海拔最高的一个格子。模拟的过程中,共有两种可能的操作:

\(move\) \(k\)无人机移动k步

\(change\) \(a\) \(b\) \(e\)第\(a\)行第\(b\)列的格子海拔修改为\(e\)。

在每次\(move\)操作后,你都需要立刻找到无人机的位置。你可以认为,每次移动的三个目标位置海拔互不相同,因此每一步移动都是良定义的。

solution

每次移动的步数\(k\)很大,所以可以联想到倍增跳法,即每次跳\(2^k\)步。

但步数不太好维护,我们考虑将移动\(k\)步转化为先暴力跳到第一列,再从第一列出发跳若干圈,最后在暴力跳剩下的不到一圈的步数。显然2段暴力跳的复杂度仅为\(O(c)\),可以接受

那么问题转化为了要维护从第一列的每一个位置出发跳\(2^k\)圈后所处的位置。本题在\(y轴\)上的跳法不确定,但在\(x\)轴上一直是每次向右移动一格,于是可以在列上建立线段树,每个节点(设对应的列为\(l,r\))维护第\(l\)列上每个位置,跳到第\(r+1\)列后所处的位置,就可以

t[p][i]=t[p<<1|1][t[p<<1][i]]

实现转移(其中\(t[p][i]\)表示处在第\(i\)行的点,跳过\(p\)对应的这段区间后所处的位置,在代码中是\(T[p].t[i]\))

于是根节点维护的答案就是第一列中的每个位置跳一圈后所处的位置,接下来的只不过是倍增基本套路。

每次对\((x,y)\)的修改只会影响第\(x-1\)列维护的答案,等于是线段树中的单点修改,复杂度\(O(rlog(c))\)

code

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int a[N][N],ans[N][N],R,C,m,to[N][32];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}
struct SGT{
    struct tree{
        int t[N];
    }T[N<<2];
    #define lc (p<<1)
    #define rc (p<<1|1)
    #define mid (l+r>>1)
    inline void pushup(int p){
        for(int i=1;i<=R;++i)
            T[p].t[i]=T[rc].t[T[lc].t[i]];
    }
    inline void build(int p,int l,int r){
        if(l==r){
            for(int i=1;i<=R;++i) T[p].t[i]=ans[l][i];
            return ;
        }
        build(lc,l,mid);
        build(rc,mid+1,r);
        pushup(p);
    }
    inline void update(int p,int l,int r,int x){
        if(l==r){
            for(int i=1;i<=R;++i) T[p].t[i]=ans[l][i];
            return ;
        }
        if(x<=mid) update(lc,l,mid,x);
        else update(rc,mid+1,r,x);
        pushup(p);
    }
    #undef lc
    #undef rc
    #undef mid
}T;
inline void work(int &x,int &y){
    int yy=y==C?1:y+1,x1=x>1?x-1:R,x2=x,x3=x==R?1:x+1;
    int ans=a[x1][yy],pos=x1;
    if(a[x2][yy]>ans) ans=a[x2][yy],pos=x2;
    if(a[x3][yy]>ans) ans=a[x3][yy],pos=x3;
    x=pos;y=yy;
}
int main(){
    R=read();C=read();
    for(int i=1;i<=R;++i)
        for(int j=1;j<=C;++j)
            a[i][j]=read();
    for(int i=1;i<=R;++i){
        for(int j=1;j<=C;++j){
            int x=i,y=j;work(x,y);
            ans[j][i]=x;
        }
    }
    T.build(1,1,C);
    for(int i=1;i<=R;++i) to[i][0]=T.T[1].t[i];
    for(int j=1;j<=30;++j)
        for(int i=1;i<=R;++i)
            to[i][j]=to[to[i][j-1]][j-1];
    m=read();
    int nx=1,ny=1;
    while(m--){
        char s[10];scanf("%s",s);
        if(s[0]=='c'){
            int x=read(),y=read(),e=read();
            a[x][y]=e;y=y>1?y-1:C;
            for(int i=1;i<=R;++i){
                int t1=i,t2=y;
                work(t1,t2);
                ans[y][i]=t1;
            }
            T.update(1,1,C,y);
            for(int i=1;i<=R;++i) to[i][0]=T.T[1].t[i];
            for(int j=1;j<=30;++j)
                for(int i=1;i<=R;++i)
                    to[i][j]=to[to[i][j-1]][j-1];
        }
        if(s[0]=='m'){
            int k=read();
            while(k&&ny!=1) work(nx,ny),k--;
            int circle=k/C;k=k%C;
            for(int i=30;i>=0;--i) if(circle&(1<<i)) circle^=(1<<i),nx=to[nx][i];
            while(k--) work(nx,ny);
            printf("%d %d\n",nx,ny);
        }
    }
    return 0;
}