peiwenjun's blog 没有知识的荒原

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\) 的复杂度,

先用滚动数组优化:

\[dp_i=\min_{j-1\le k\le i-1}\big(dp'_k+cost(k,i)\big)+c_i \]

升序遍历 \(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;
}

posted on 2022-07-26 09:33  peiwenjun  阅读(15)  评论(0)    收藏  举报

导航