題目
題解
聽說是非常經典的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);
}