[ZJOI2010]基站選址(DP+線段樹)

題目

洛谷P2605

題解

聽說是非常經典的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);
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章