题解:P6797 「StOI-2」不朽的逃亡者
题目分析
设
不选择任何人时,就是普通的网格 DP:
接下来考虑加入一个人的贡献,假设这个人保护的矩形为
因为矩形内部的危险值全部变成
假设从
枚举
也是维护一个前缀最小值。
需要快速求一整列或一整行上的区间最小值,分别对每一列和每一行建立线段树。
由于每个人对应的查询区间始终不变,可以预处理区间在线段树上被拆成了哪些节点,之后直接访问这些节点的最小值。
处理完所有矩形后,再进行一次普通网格 DP,使当前位置继续向右、向下扩展。
重复上述过程至多
因为每轮都有
认为
能用
参考代码
:::info[Code]
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb emplace_back
const int N=205,inf=1e18;
int n,m,k,w,a[N][N],f[N][N],g[N][N];
struct rect{int ax,bx,ay,by;}q[N];
vector<int>seg,C[N],R[N];
struct Tree{
int t[N][N<<2],s[N][N<<2];
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
#define mid ((l+r)>>1)
inline void buildC(int y,int l,int r,int p=1){
if(l==r){t[y][p]=g[l][y];return;}
buildC(y,lson),buildC(y,rson);
t[y][p]=min(t[y][p<<1],t[y][p<<1|1]);
}
inline void buildR(int x,int l,int r,int p=1){
if(l==r){s[x][p]=g[x][l];return;}
buildR(x,lson),buildR(x,rson);
s[x][p]=min(s[x][p<<1],s[x][p<<1|1]);
}
inline void build(){
for(int y=1;y<=m;++y)buildC(y,1,n);
for(int x=1;x<=n;++x)buildR(x,1,m);
}
inline void queryC(int ql,int qr,int l=1,int r=n,int p=1){
if(ql<=l&&r<=qr){seg.pb(p);return;}
if(ql<=mid)queryC(ql,qr,lson);
if(qr>mid) queryC(ql,qr,rson);
}
inline void queryR(int ql,int qr,int l=1,int r=m,int p=1){
if(ql<=l&&r<=qr){seg.pb(p);return;}
if(ql<=mid)queryR(ql,qr,lson);
if(qr>mid) queryR(ql,qr,rson);
}
}T;
inline void solve(int dp[][N]){
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
dp[i+1][j]=min(dp[i+1][j],dp[i][j]+a[i][j]);
dp[i][j+1]=min(dp[i][j+1],dp[i][j]+a[i][j]);
}
}
inline void OIer_WiFi(){
scanf("%lld%lld%lld%lld",&n,&m,&k,&w);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%lld",&a[i][j]);
for(int i=1;i<=k;++i){
auto &[ax,bx,ay,by]=q[i];
scanf("%lld%lld%lld%lld",&ax,&bx,&ay,&by);
seg.clear(),T.queryC(ax,bx),C[i]=seg;
seg.clear(),T.queryR(ay,by),R[i]=seg;
}
memset(g,0x3f,sizeof g);
g[1][1]=0,solve(g);
for(int i=1;i<=min(k,w);++i){
T.build(),memcpy(f,g,sizeof f);
for(int j=1;j<=k;++j){
auto [ax,bx,ay,by]=q[j];
int cur=inf;
for(int y=ay;y<=by;++y){
for(int p:C[j]) cur=min(cur,T.t[y][p]);
f[bx+1][y]=min(f[bx+1][y],cur);
}
cur=inf;
for(int x=ax;x<=bx;++x){
for(int p:R[j]) cur=min(cur,T.s[x][p]);
f[x][by+1]=min(f[x][by+1],cur);
}
} solve(f);
if(!memcmp(f,g,sizeof f))break;
memcpy(g,f,sizeof g);
}
printf("%lld",min(g[n+1][m],g[n][m+1]));
}
signed main(){OIer_WiFi();}