洛谷题目传送门
老话说的好,看见网格图,就想分治
我们对行进行分治
设当前分支节点是k,区间是
l
[
k
]
l[k]
l[k]行到
r
[
k
]
r[k]
r[k]行,他的分治子节点分别是
l
s
o
n
[
k
]
,
r
s
o
n
[
k
]
lson[k],rson[k]
lson[k],rson[k]
对于一个分治节点,我们处理出
f
[
k
]
[
x
]
[
y
]
f[k][x][y]
f[k][x][y],表示从
(
l
[
k
]
,
x
)
(l[k],x)
(l[k],x)到
(
r
[
k
]
,
y
)
(r[k],y)
(r[k],y)的最短路
合并数组的时候,因为从上至下的路径一定会经过
m
i
d
=
(
l
+
r
)
>
>
1
mid=(l+r)>>1
mid=(l+r)>>1,也就是分治的中点
那么我们可以枚举这条路径经过第
m
i
d
mid
mid行的哪个位置,然后就是从
x
x
x到这个点+从这个点到
y
y
y
而这两个东西都是我们已经计算过得
复杂度
O
(
m
3
n
l
o
g
n
)
O(m^3nlogn)
O(m3nlogn)
不可能通过
考虑优化,易证明
f
f
f数组具有二维决策单调性
证明的话,考虑把两条相交的路径分开,一定会更优
具体,设
f
[
k
]
[
x
]
[
y
]
f[k][x][y]
f[k][x][y]是从
(
m
i
d
,
p
[
k
]
[
x
]
[
y
]
)
(mid,p[k][x][y])
(mid,p[k][x][y])转移的
那么
p
[
k
]
[
x
−
1
]
[
y
]
≤
p
[
k
]
[
x
]
[
y
]
≤
p
[
x
]
[
y
+
1
]
p[k][x-1][y]\leq p[k][x][y] \leq p[x][y+1]
p[k][x−1][y]≤p[k][x][y]≤p[x][y+1]
复杂度
O
(
m
2
n
l
o
g
n
)
O(m^2nlogn)
O(m2nlogn)
时间大概是可以过的,但是空间为
n
l
o
g
n
m
2
nlognm^2
nlognm2,开不下
考虑对分治的叶子结点分块,也就是当
r
−
l
<
B
r-l<B
r−l<B的时候,对这个分治区间暴力做
怎么暴力做呢
枚举最上边一行的起点位置
S
S
S,设
d
i
s
[
i
]
[
j
]
dis[i][j]
dis[i][j]表示从
S
S
S到
(
i
,
j
)
(i,j)
(i,j)的最短路
设
L
i
n
e
[
i
]
[
j
]
Line[i][j]
Line[i][j]为从
(
i
,
j
)
(i,j)
(i,j)向下连边的边权
那么
f
[
i
]
[
j
]
=
m
i
n
(
f
[
i
−
1
]
[
k
]
+
L
i
n
e
[
i
−
1
]
[
k
]
+
d
i
s
[
(
i
,
k
)
]
[
(
i
,
j
)
]
)
f[i][j]=min(f[i-1][k]+Line[i-1][k]+dis[(i,k)][(i,j)])
f[i][j]=min(f[i−1][k]+Line[i−1][k]+dis[(i,k)][(i,j)])
后边的拆开之后就可以维护前缀min
做到
O
(
n
m
2
)
O(nm^2)
O(nm2)
总的就能通过了
#include<bits/stdc++.h>
using namespace std;
const int N = 5050,M =207;
typedef long long LL;
const int INF = 1e9+7;
int Rgh[N][M],Row[N][M],Dwn[N][M];
int h[M];
int n,m,B=16;
int dis[1090][M][M];
int p[M][M];
void pushup(int rot,int mid)
{
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
p[i][j]=0;
for(int i=1;i<=m;i++)
{
for(int j=m;j>=1;j--)
{
int l=1,r=m,wal=INF;
if(p[i-1][j]) l=max(l,p[i-1][j]);
if(p[i][j+1]) r=min(r,p[i][j+1]);
for(int k=l;k<=r;k++)
{
int v=dis[rot<<1][i][k]+dis[rot<<1|1][k][j];
v=v+Dwn[mid][k];
if(v<wal)
{
wal=v;
p[i][j]=k;
}
}
dis[rot][i][j]=wal;
}
}
}
void Upd(int f[M][M],int l,int r)
{
for(int S=1;S<=m;S++)
{
for(int i=1;i<=m;i++)
h[i]=abs(Row[l][i]-Row[l][S]);
for(int i=l+1;i<=r;i++)
{
for(int j=1;j<=m;j++)
h[j]+=Dwn[i-1][j];
int Bet=h[1];
for(int j=2;j<=m;j++)
{
h[j]=min(h[j],Bet+Row[i][j]);
Bet=min(Bet,h[j]-Row[i][j]);
}
Bet=h[m]+Row[i][m];
for(int j=m-1;j>=1;j--)
{
h[j]=min(h[j],Bet-Row[i][j]);
Bet=min(Bet,h[j]+Row[i][j]);
}
}
for(int i=1;i<=m;i++)
f[S][i]=h[i];
}
}
void Build(int k,int l,int r)
{
if(r-l<=B)
{
Upd(dis[k],l,r);
// exit(0);
return;
}
int mid=(l+r)>>1;
Build(k<<1,l,mid);
Build(k<<1|1,mid+1,r);
pushup(k,mid);
}
void Updy(int k,int l,int r,int x)
{
if(r-l<=B)
{
Upd(dis[k],l,r);
return;
}
int mid=(l+r)>>1;
if(x<=mid) Updy(k<<1,l,mid,x);
else Updy(k<<1|1,mid+1,r,x);
pushup(k,mid);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<m;j++)
{
scanf("%d",&Rgh[i][j]);
Row[i][j+1]=Row[i][j]+Rgh[i][j];
}
}
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&Dwn[i][j]);
Build(1,1,n);
// exit(0);
int q;
cin>>q;
while(q--)
{
int opt,x,y;
scanf("%d %d %d",&opt,&x,&y);
x++;y++;
if(opt==3) printf("%d\n",dis[1][x][y]);
else
{
int v;
scanf("%d",&v);
if(opt==1)
{
int det=v-Rgh[x][y];
Rgh[x][y]=v;
for(int i=y+1;i<=m;i++)
Row[x][i]+=det;
Updy(1,1,n,x);
}
else
{
Dwn[x][y]=v;
Updy(1,1,n,x);
}
}
}
return 0;
}