P5897 [IOI 2013] wombats

P5897 [IOI 2013] wombats

Description

给定一张 \(n\times m\) 的网格图,并有 \(Q_1\) 次修改和 \(Q_2\) 次查询,每次修改会更改一条边的边权,每次查询 \((1,x)\rightarrow (n,y)\) 的最短路(只能向下,左右随意)。修改和查询交替进行。

\(n\leq 5000,m\leq 200,Q_1\leq 500,Q_2\leq 200000\)

Solution

考虑没有修改怎么做。

\(f_{x,i,j}\) 表示 \((1,x)\rightarrow (i,j)\) 的最短路。得到转移 \(f_{x,i,j}+b_{i,j}+w(i+1,j,k)\rightarrow f_{x,i+1,k}\),其中 \(b_{i,j}\)\((i,j)\) 向下这条边的边权,\(w(i+1,j,k)\) 表示 \((i+1,j)\rightarrow (i+1,k)\) 的路径权值。

直接做是 \(O(nm^3)\) 的。但发现最优路径之间不会交叉,即 \(f\) 具有决策单调性。于是优化到 \(O(nm^2)\)

现在加上修改。设 \(f_{l,r,i,j}\) 表示 \((l,i)\rightarrow (r,j)\) 的最短路,容易发现 \(f_{l,r}\) 具有区间可加性,可以 \(O(m^2)\) 合并区间。

于是我们用线段树维护。建树复杂度 \(O(nm^2)\);每次修改影响 \(O(\log n)\) 个节点,修改复杂度 \(O(m^2 \log n)\)

但此时空间复杂度也为 \(O(nm^2)\),无法通过。

\(n\) 这一维分块,设大小为 \(B\)。对这些块建线段树,空间复杂度变为 \(O(nm+\dfrac n B m^2)\),修改复杂度变为 \(O(m^2(B+\dfrac n B))\)\(B\)\([10,20]\) 之间最优。

int n,m;
int a[N][M],b[N][M];
int g[N][M];
int L[K],R[K],C,bl[N];

struct Matrix{
    int val[M][M];

    void Init(int x){
        memset(val,x,sizeof(val));
    }

    friend Matrix operator * (Matrix &x,Matrix &y){
        Matrix z; z.Init(0x3f);
        for(int i=1;i<=m;i++) g[m+1][i]=m;
        for(int i=m;i;i--){
            g[i][0]=1;
            for(int j=1;j<=m;j++){
                for(int p=g[i][j-1];p<=g[i+1][j];p++){
                    int res=x.val[i][p]+y.val[p][j];
                    if(res<z.val[i][j]) z.val[i][j]=res,g[i][j]=p;
                }
            }
        }
        return z;
    }

    void Print(){
        for(int i=1;i<=m;i++){
            for(int j=1;j<=m;j++)
                printf("%-2d ",val[i][j]);
            puts("");
        }
    }
}tr[K];

Matrix Solve(int l,int r){
    Matrix f; f.Init(0x3f);
    for(int i=1;i<=m;i++) f.val[i][i]=0;
    for(int i=l;i<=r;i++){
        Matrix h;
        for(int j=1;j<=m;j++){
            h.val[j][j]=0;
            for(int k=j-1;k>=1;k--) h.val[j][k]=h.val[j][k+1]+a[i][k];
            for(int k=j+1;k<=m;k++) h.val[j][k]=h.val[j][k-1]+a[i][k-1];
            for(int k=1;k<=m;k++) h.val[j][k]+=b[i][k];
        }
        f=f*h;
    }
    return f;
}

void Buildtr(int p,int l,int r){
    if(l==r){
        tr[p]=Solve(L[l],R[l]);
        return;
    }
    int mid=(l+r)>>1;
    Buildtr(p<<1,l,mid);
    Buildtr(p<<1|1,mid+1,r);
    tr[p]=tr[p<<1]*tr[p<<1|1];
}

void Update(int p,int l,int r,int x){
    if(l==r){
        tr[p]=Solve(L[l],R[l]);
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) Update(p<<1,l,mid,x);
    else Update(p<<1|1,mid+1,r,x);
    tr[p]=tr[p<<1]*tr[p<<1|1];
}

signed main(){
    read(n),read(m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<m;j++)
            read(a[i][j]);
    }
    for(int i=1;i<n;i++){
        for(int j=1;j<=m;j++)
            read(b[i][j]);
    }
    for(int i=1;i<=n;i+=B){
        ++C;
        L[C]=i,R[C]=min(n,i+B-1);
        for(int j=L[C];j<=R[C];j++) bl[j]=C;
    }
    Buildtr(1,1,C);
    int Q; read(Q);
    while(Q--){
        int op; read(op);
        if(op==1){
            int x,y;
            read(x),read(y); x++,y++;
            read(a[x][y]);
            Update(1,1,C,bl[x]);
        }
        else if(op==2){
            int x,y;
            read(x),read(y); x++,y++;
            read(b[x][y]);
            Update(1,1,C,bl[x]);
        }
        else{
            int x,y; read(x),read(y); x++,y++;
            printf("%d\n",tr[1].val[x][y]);
        }
    }
    return 0;
}










posted @ 2025-06-09 16:17  XP3301_Pipi  阅读(15)  评论(0)    收藏  举报
Title