题目
题解
听说是非常经典的DP+线段树题,就来瞻仰一下,果然思维难度还是不小的
这道题也让我对线段树有了更深的了解,不仅是明面上是区间修改的需要用到线段树,甚至像DP这种算法,当算法时间复杂度太高的时候,可以用线段树来处理DP的数据,达到简化DP的目的
这道题的递推公式:f[i]=min{f[i-1]+w[i-1][j]}+c[i]
f[i]为假如第i个点造基站的最小费用,w[x][y]为x和y造了基站后中间的收不到信号的赔偿费用
一看到递推式中出现w这种二维数组,一般直接DP,大多TLE,但是这里可以看到的是当i增加的时候,f数组是不会变得,只有w数组会变,会增加(因为最后一个基站的位置往后移了,会增加中间的赔偿费用),所以我们记录第i个节点上基站最左和最右到达的基站数,然后假如计算完i后,i+1,对R[x]=i的点(这些点就是最远只能到达i号节点,到达不了i+1号节点)的L[x]前全部加上w[x](之前i上的基站能覆盖到x,但i+1的基站就覆盖不到x了,L[x]向左的基站也覆盖不到a,所以a就被抛弃了,得加上他的损失值)
小trick:
- 因为最后的结果是 f[n],但是第n个基站不一定选取,可以在最后添加一个肯定选取的虚拟基站,n+1,距离为INF,不对前面的基站造成影响~~
别忘了n+1后k也要+1啊,Wa了我无数次 - 和一般的线段树相比,可能还要加判断L>R的情况,否则会陷入死循环
代码
#include<bits/stdc++.h>
#define ll long long
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=200010;
const int INF=0x3f3f3f3f;
int tre[maxn<<2],lazy[maxn<<2];
int d[maxn],s[maxn];
int w[maxn],c[maxn],l[maxn],r[maxn],f[maxn];
int ans;
vector<int> g[maxn];
inline void pushup(int r)
{
tre[r]=min(tre[r<<1],tre[r<<1|1]);
}
void build(int rt,int l,int r)
{
lazy[rt]=0;
if(l==r)
{
tre[rt]=f[l];
return;
}
int mid=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void pushdown(int rt,int l,int r)
{
if(lazy[rt]&&l!=r)
{
int a=lazy[rt];
lazy[rt<<1]+=a;
lazy[rt<<1|1]+=a;
tre[rt<<1]+=a;
tre[rt<<1|1]+=a;
lazy[rt]=0;
}
}
void update(int rt,int l,int r,int x,int y,int z)
{
if(x>y) return;
pushdown(rt,l,r);
if(x<=l&&r<=y)
{
lazy[rt]+=z;
tre[rt]+=z;
return;
}
int mid=(l+r)>>1;
if(x<=mid) update(lson,x,y,z);
if(mid<y) update(rson,x,y,z);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y)
{
//cout<<rt<<' '<<l<<' '<<r<<' '<<x<<' '<<y<<endl;
if(x>y) return 0;pushdown(rt,l,r);
int res=0x3f3f3f3f;
if(x<=l&&r<=y) return tre[rt];
int mid=(l+r)>>1;
if(x<=mid) res=min(res,query(lson,x,y));
if(mid<y) res=min(res,query(rson,x,y));
return res;
}
int main()
{
int n,k;
scanf("%d %d",&n,&k);k++;
for(int i=2;i<=n;i++) scanf("%d",&d[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
d[++n]=INF;;w[n]=INF;
for(int i=1;i<=n;i++){
l[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
r[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d;
if(d[r[i]]>d[i]+s[i]) r[i]--;
g[r[i]].push_back(i);
}
int t=0;
for(int i=1;i<=n;i++){
f[i]=t+c[i];
for(int j:g[i]) t+=w[j];
}
ans=f[n];
if(k>1){
for(int i=2;i<=k;i++){
build(1,1,n);
for(int j=1;j<=n;j++){
f[j]=query(1,1,n,1,j-1)+c[j];
for(int p:g[j]) update(1,1,n,1,l[p]-1,w[p]);
}
ans=min(ans,f[n]);
}
}
printf("%d\n",ans);
}