【洛谷P2605】基站选址

题目

题目链接:https://www.luogu.com.cn/problem/P2605
(N) 个村庄坐落在一条直线上,第 (i(i>1)) 个村庄距离第 (1) 个村庄的距离为 (D_i)。需要在这些村庄中建立不超过 (K) 个通讯基站,在第 (i) 个村庄建立基站的费用为 (C_i)。如果在距离第 (i) 个村庄不超过 (S_i) 的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第 (i) 个村庄没有被覆盖,则需要向他们补偿,费用为 (W_i)。现在的问题是,选择基站的位置,使得总费用最小。
(Nleq 20000,Kleq 100)

思路

(f[i][j]) 表示在前 (i) 个村庄,安装了 (j) 个通讯基站的最小代价。
考虑枚举上一次选择安装基站的位置 (k),有转移

[f[i][j]=min_{k<i}left(f[k][j-1]+sum^{i-1}_{l=k+1}[d_l+s_l<d_i] imes [d_l-s_l>d_k] imes 1 ight) ]

直接转移是 (O(n^2k)) 的,看到区间最小值,考虑维护 (k) 棵线段树优化转移。
对于一个位置 (k(k<i)),如果满足 (d_k+s_k<d_i),那么对于 (d_k-s_k>d_j) 的所有 (j),从 (f[j][l-1]) 转移到 (f[i][l]) 的时候都会算上 (w_k) 的贡献。所以我们可以按照 (d_i-s_i) 从小到大排序,当我们枚举到 (i) 时,维护一个指针 (j),把所有 (d_j+s_j<d_i)(j),在线段树上区间 ([1,d_j-s_j-1]) 加上 (w_j) 即可。
时间复杂度 (O(nklog n))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=20010,M=101,Inf=2e9;
int n,m,ans,sum,d[N],c[N],s[N],w[N],id[N],L[N],f[N][M];

bool cmp1(int x,int y)
{
	return d[x]+s[x]<d[y]+s[y];
}

bool cmp2(int x,int y)
{
	return d[x]-s[x]>d[y]-s[y];
}

struct SegTree
{
	int lazy[N*4],minn[N*4];
	
	void pushdown(int x)
	{
		if (lazy[x])
		{
			minn[x*2]+=lazy[x]; minn[x*2+1]+=lazy[x];
			lazy[x*2]+=lazy[x]; lazy[x*2+1]+=lazy[x];
			lazy[x]=0;
		}
	}
	
	void update(int x,int l,int r,int ql,int qr,int v)
	{
		if (ql>qr) return;
		if (ql<=l && qr>=r)
			return (void)(lazy[x]+=v,minn[x]+=v);
		pushdown(x);
		int mid=(l+r)>>1;
		if (ql<=mid) update(x*2,l,mid,ql,qr,v);
		if (qr>mid) update(x*2+1,mid+1,r,ql,qr,v);
		minn[x]=min(minn[x*2],minn[x*2+1]);
	}
	
	int query(int x,int l,int r,int ql,int qr)
	{
		if (ql>qr) return 0;
		if (ql<=l && qr>=r) return minn[x];
		pushdown(x);
		int mid=(l+r)>>1,res=Inf;
		if (ql<=mid) res=min(res,query(x*2,l,mid,ql,qr));
		if (qr>mid) res=min(res,query(x*2+1,mid+1,r,ql,qr));
		return res;
	}
}seg[M];

int main()
{
	cerr<<(sizeof(seg)+sizeof(f))/1024/1024<<"
";
	scanf("%d%d",&n,&m);
	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]);
	for (int i=1;i<=n;i++)
	{
		id[i]=i; ans+=w[i];
		L[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d-1;
	}
	sort(id+1,id+1+n,cmp1);
	for (int i=1,k=1;i<=n;i++)
	{
		for (;k<=n && d[id[k]]+s[id[k]]<d[i];k++)
		{
			sum+=w[id[k]];
			for (int j=0;j<=m;j++)
				seg[j].update(1,1,n,1,L[id[k]],w[id[k]]);
		}
		f[i][1]=c[i]+sum;
		seg[1].update(1,1,n,i,i,f[i][1]);
		for (int j=2;j<=m;j++)
		{
			f[i][j]=seg[j-1].query(1,1,n,1,i-1)+c[i];
			seg[j].update(1,1,n,i,i,f[i][j]);
		}
	}
	sum=0;
	sort(id+1,id+1+n,cmp2);
	for (int i=n;i>=1;i--) w[i]+=w[i+1];
	for (int i=n,k=1;i>=1;i--)
	{
		for (;k<=n && d[id[k]]-s[id[k]]>d[i];k++)
			sum+=w[id[k]]-w[id[k]+1];
		for (int j=1;j<=m;j++)
			ans=min(ans,f[i][j]+sum);
	}
	cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/15114151.html