P2605 [ZJOI2010]基站选址 题解
题目描述
\(n\) 个村庄,第 \(i\) 个村庄到第 \(1\) 个村庄的距离为 \(d_i\) 。
你需要建立不超过 \(k\) 个通讯基站,在第 \(i\) 个村庄建立的代价为 \(c_i\) 。
如果在距离第 \(i\) 个村庄 \(s_i\) 的范围内有通讯基站,那么村庄被覆盖。
如果第 \(i\) 个村庄未被覆盖,需要补偿 \(w_i\) 的费用。
求建立基站需要花费的最小代价总和。
数据范围
- \(1\le n\le 2\cdot 10^4,k\le\min(n,100)\) 。
- \(1\le d_i,s_i\le 10^9\) ,保证 \(d_i\) 严格递增。
- \(1\le c_i,w_i\le 10^4\) 。
时间限制 \(\texttt{5s}\) ,空间限制 \(\texttt{125MB}\) 。
分析
\(d_i,s_i\) 看上去就没什么用,直接二分求出 \([st_i,ed_i]\) , 表示如果想要覆盖第 \(i\) 个村庄,需要在这个范围内建立通讯基站。
\(dp_{i,j}\) 表示仅考虑前 \(i\) 个村庄,总共建立 \(j\) 个通讯基站,并且最后一个建在 \(i\) 的最小代价。
转移方程 \(dp_{i,j}=\min\limits_{j-1\le k\le i-1}\big(dp_{k,j-1}+cost(k,i)\big)+c_i\) ,其中 \(cost(k,i)\) 是第 \([k,i]\) 个村庄中未被覆盖村庄补偿费用之和。
状态数 \(nk\) 可以接受,但是 \(\mathcal O(n)\) 的转移太慢了,而且还没考虑计算 \(cost\) 的复杂度,
先用滚动数组优化:
升序遍历 \(i\) ,显然我们不需要考虑 \(ed_x\ge i\) 的村庄 \(x\) 。
当 \(i\to i+1\) 时,对于所有 \(ed_x=i\) 的村庄 \(x\) ,如果 \(st_x\lt k\) ,那么 \(x\) 无法被覆盖。
将 \(dp'_k+cost(k,i)\) 放在线段树的第 \(k\) 个叶子上,则只需给第 \([1,st_x-1]\) 个叶子加上 \(w_x\) 。
然后查询区间 \(\min\) 即可,注意 \(j=1\) 要特殊处理。
统计答案时可以新建一个坐标 \(\inf\) ,建设花费为 \(0\) ,覆盖距离为 \(0\) ,补偿花费 \(\inf\) 的虚拟村庄(这个村庄必须建立基站且不会覆盖其他村庄),最后访问它的 \(\texttt{dp}\) 值即可。
时间复杂度 \(\mathcal O(kn\log n)\) 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e4+5,inf=1e18;
int k,n,res;
int c[maxn],d[maxn],s[maxn],w[maxn];
int dp[maxn],st[maxn],ed[maxn];
vector<int> vec[maxn];
struct node
{
int l,r,add,mn;
}f[4*maxn];
void pushadd(int p,int v)
{
f[p].add+=v,f[p].mn+=v;
}
void pushdown(int p)
{
if(!f[p].add) return ;
pushadd(2*p,f[p].add),pushadd(2*p+1,f[p].add),f[p].add=0;
}
void pushup(int p)
{
f[p].mn=min(f[2*p].mn,f[2*p+1].mn);
}
void build(int p,int l,int r)
{
f[p].l=l,f[p].r=r,f[p].add=0;
if(l==r) return f[p].mn=dp[l],void();
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
pushup(p);
}
void modify(int p,int l,int r,int v)
{
if(l<=f[p].l&&f[p].r<=r) return pushadd(p,v);
if(l>f[p].r||r<f[p].l) return ;
pushdown(p);
modify(2*p,l,r,v);
modify(2*p+1,l,r,v);
pushup(p);
}
int query(int p,int l,int r)
{
if(l<=f[p].l&&f[p].r<=r) return f[p].mn;
if(l>f[p].r||r<f[p].l) return inf;
pushdown(p);
return min(query(2*p,l,r),query(2*p+1,l,r));
}
signed main()
{
scanf("%lld%lld",&n,&k);
for(int i=2;i<=n;i++) scanf("%lld",&d[i]);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++) scanf("%lld",&s[i]);
for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
n++,k++,d[n]=w[n]=inf;
for(int i=1;i<=n;i++)
{
st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
ed[i]=upper_bound(d+1,d+n+1,d[i]+s[i])-d-1;
vec[ed[i]].push_back(i);
}
for(int i=1,cur=0;i<=n;i++)
{
dp[i]=cur+c[i];
for(auto x:vec[i]) cur+=w[x];
}
res=dp[n];
for(int j=2;j<=k;j++)
{
build(1,1,n);
for(int i=j;i<=n;i++)
{
dp[i]=query(1,j-1,i-1)+c[i];
for(auto x:vec[i]) modify(1,1,st[x]-1,w[x]);
}
res=min(res,dp[n]);
}
printf("%lld\n",res);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16519690.html
浙公网安备 33010602011771号