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;
}

浙公网安备 33010602011771号