题解 洛谷 P5897 【[IOI2013]wombats 】

发现行数比较大,列数比较小,考虑用线段树来维护行的情况,记录列对应的信息。

(f_{i,j}) 在当前线段树节点维护的区间,从上边界 (i) 位置到下边界 (j) 位置的最小花费,为方便合并,实际处理的为跨过了下边界的最小花费。

每次像 (Floyd) 一样合并的复杂度为 (O(m^3)),所以要考虑优化。发现任意两条最优路径不会相交,因为如果相交,其中一条一定能调整为更优的路径。因此转移有决策单调性,这样每次合并的复杂度就为 (O(m^2)) 了。

发现开不下线段树的空间。于是对行进行分块,对于每块进行 (O(nm^2))(DP) 来处理信息,线段树的叶子节点就是每个块。

设块大小为 (S),修改次数为 (T),询问次数为 (Q),则时间复杂度为 (O(nm^2+Tm^2(logfrac{n}{S}+S)+Q)),空间复杂度为 (O(nm+frac{n}{S}m^2))

#include<bits/stdc++.h>
#define maxn 5010
#define maxm 210
#define maxt 1010
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,q,S=20,root=1;
int bel[maxn],L[maxn],R[maxn],v1[maxn][maxm],v2[maxn][maxm],p[maxm][maxm];
struct node
{
    int f[maxm][maxm];
    node()
    {
        memset(f,0x3f,sizeof(f));
    }
    void get(int u,int d)
    {
        for(int i=1;i<=m;++i)
        {
            for(int j=i,v=0;j<=m;++j,v+=v1[u][j-1]) f[i][j]=v;
            for(int j=i,v=0;j;--j,v+=v1[u][j]) f[i][j]=v;
            for(int j=1;j<=m;++j) f[i][j]+=v2[u][j];
            for(int k=u+1;k<=d;++k)
            {
                for(int j=2;j<=m;++j) f[i][j]=min(f[i][j],f[i][j-1]+v1[k][j-1]);
                for(int j=m-1;j;--j) f[i][j]=min(f[i][j],f[i][j+1]+v1[k][j]);
                for(int j=1;j<=m;++j) f[i][j]+=v2[k][j];
            }
        }
    }
}t[maxt];
node operator +(const node &a,const node &b)
{
    node c;
    memset(p,0,sizeof(p));
    for(int i=1;i<=m;++i)
        for(int j=m;j;--j)
            for(int k=(p[i-1][j]?p[i-1][j]:1);k<=(p[i][j+1]?p[i][j+1]:m);++k)
                if(c.f[i][j]>a.f[i][k]+b.f[k][j])
                    c.f[i][j]=a.f[i][k]+b.f[k][j],p[i][j]=k;
    return c;
}
void build(int l,int r,int cur)
{
    if(l==r)
    {
        t[cur].get(L[l],R[l]);
        return;
    }
    build(l,mid,ls),build(mid+1,r,rs),t[cur]=t[ls]+t[rs];
}
void update(int l,int r,int pos,int cur)
{
    if(l==r)
    {
        t[cur].get(L[l],R[l]);
        return;
    }
    if(pos<=mid) update(l,mid,pos,ls);
    else update(mid+1,r,pos,rs);
    t[cur]=t[ls]+t[rs];
}
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;++i)
        for(int j=1;j<m;++j)
            read(v1[i][j]);
    for(int i=1;i<n;++i)
        for(int j=1;j<=m;++j)
            read(v2[i][j]);
    for(int i=1;i<=n;++i) bel[i]=(i-1)/S+1;
    for(int i=1;i<=bel[n];++i) L[i]=S*(i-1)+1,R[i]=S*i;
    R[bel[n]]=n,build(1,bel[n],root),read(q);
    while(q--)
    {
        int opt,x,y,v;
        read(opt),read(x),read(y),x++,y++;
        if(opt==1) read(v1[x][y]),update(1,bel[n],bel[x],root);
        if(opt==2) read(v2[x][y]),update(1,bel[n],bel[x],root);
        if(opt==3) printf("%d
",t[root].f[x][y]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/13722391.html