BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835

题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位 置,使得总费用最小。

思路:

另外,程序中的n=n+1,m=m+1。因
为每次使用f[n]更新答案的,而f[n]的含义是在n位置建立一个通讯站,但是显然有时候最优值并不是一定要在n建立一个。将n+1之后,m+1,则
m+1个必然建立在n+1,而这一个在我们计算st和ed数组时看出他们是不对前面的有影响的。因此统计f[n+1]才是正确的。

struct Node
{
    int L,R;
    i64 Min,det;

    void set(i64 x)
    {
        det+=x;
        Min+=x;
    }
};

Node a[N<<2];

void pushUp(int t)
{
    if(a[t].L==a[t].R) return;
    a[t].Min=min(a[t*2].Min,a[t*2+1].Min);
}

i64 f[N],ans;

void build(int t,int L,int R)
{
    a[t].L=L;
    a[t].R=R;
    a[t].det=0;
    if(L==R)
    {
        a[t].Min=f[L];
        return;
    }
    int mid=(L+R)>>1;
    build(t*2,L,mid);
    build(t*2+1,mid+1,R);
    pushUp(t);
}

void pushDown(int t)
{
    if(a[t].L==a[t].R) return;
    if(a[t].det)
    {
        a[t*2].set(a[t].det);
        a[t*2+1].set(a[t].det);
        a[t].det=0;
    }
}

void add(int t,int L,int R,i64 x)
{
    if(L>a[t].R||R<a[t].L) return;

    if(L<=a[t].L&&a[t].R<=R)
    {
        a[t].set(x);
        return;
    }

    pushDown(t);
    add(t*2,L,R,x);
    add(t*2+1,L,R,x);
    pushUp(t);
}

i64 query(int t,int L,int R)
{
    if(L>a[t].R||R<a[t].L) return inf;
    if(L<=a[t].L&&a[t].R<=R) return a[t].Min;

    pushDown(t);
    i64 ans=min(query(t*2,L,R),query(t*2+1,L,R));
    pushUp(t);
    return ans;
}

int n,m,d[N],c[N],s[N],w[N];
int st[N],ed[N];
vector<int> V[N]; 

int getL(int x,int pos)
{
    int low=1,high=pos,mid;
    while(low<=high)
    {
        mid=(low+high)>>1;
        if(d[mid]>=x) high=mid-1;
        else low=mid+1;
    }
    if(high>=1&&d[high]>=x) return high;
    return low;
}

int getR(int x,int pos)
{
    int low=pos,high=n,mid;
    while(low<=high)
    {
        mid=(low+high)>>1;
        if(d[mid]>x) high=mid-1;
        else low=mid+1;
    }
    if(low<=n&&d[low]<=x) return low;
    return high;
}

void init()
{
    int i;
    FOR1(i,n)
    {
        st[i]=getL(d[i]-s[i],i);
        ed[i]=getR(d[i]+s[i],i);
        V[ed[i]].pb(i);
    }
}

void DP()
{
    build(1,0,n);
    int i,j,k;
    FOR1(i,n)
    {
        f[i]=query(1,0,i-1)+c[i];
        FOR0(j,SZ(V[i]))
        {
            k=V[i][j];
            add(1,0,st[k]-1,w[k]);
        }
    }
    upMin(ans,f[n]);
}

int main()
{
    RD(n,m);
    int i;
    for(i=2;i<=n;i++) RD(d[i]);
    FOR1(i,n) RD(c[i]);
    FOR1(i,n) RD(s[i]);
    FOR1(i,n) RD(w[i]);
    FOR1(i,n) f[i]=inf;
    init();
    ans=inf; n++; m++;
    FOR1(i,m) DP();
    PR(ans);
}

BZOJ 1835 基站选址(线段树优化DP),布布扣,bubuko.com

时间: 2024-08-09 10:44:38

BZOJ 1835 基站选址(线段树优化DP)的相关文章

bzoj 1835 基站选址(线段树优化Dp)

Description 题意:有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di 需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci 如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了 如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi 现在的问题是,选择基站的位置,使得总费用最小. Solution 首先可以想到dp,用dp[i][j]表示前i个村庄建了j个通讯站且第j个建在i处 dp[i][j]=min(dp[k][

【bzoj3939】[Usaco2015 Feb]Cow Hopscotch 动态开点线段树优化dp

题目描述 Just like humans enjoy playing the game of Hopscotch, Farmer John's cows have invented a variant of the game for themselves to play. Being played by clumsy animals weighing nearly a ton, Cow Hopscotch almost always ends in disaster, but this has

[Poi2010]Monotonicity 2 (线段树优化DP)

题目描述 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k].选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1].求出L的最大值. 输入 第一行两个正整数,分别表示N和K (N, K <= 500,000).第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6).第三行给出K个空格隔开关系符号(>.<或=),第i个表示s[i]. 输出 一个正整数,表示L的

降临(线段树优化dp)

降临 选定点i会有代价\(c_i\),如果一个区间j内的点全被选择,就可以获得回报\(p_j\).点数和区间个数\(<1e5\). 还以为是线段树优化网络流(50万个点200万条边看上去很可做的样子毕竟lbn说过网络流20万万条边完全没问题),没想到是个线段树dp. (虽然这两个线段树完全扯不上关系) 用\(f[i][j]\)表示考虑到第i个点,向左最近的尚未选定的点为j时的最大值.那么,i+1可以选也可以不选.不选i时,\(f[i][j]\rightarrow f[i+1][i+1]\).选i

【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

[BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述. 第

Codeforces 833B 线段树优化 dp

Codeforces  833B  The Bakery 题意: n 个数要分成 k 块,每块的价值是其不同数的个数,问价值和最大是多少. tags: dp[i][j]表示前 j 个数分成 i 块的最大权值和,转移: dp[i][j] = max( dp[i-1][k] + val[k+1][j] ) , k是 1~j . 但这个过程其实并不好转移,要利用累加的特点,用线段树进行优化 (感觉我不看题解是想不到的,2333) 大概就是,对于第 i 层,我们假定已经知道了第 i-1 层,也就是求出了

【8.23校内测试】【贪心】【线段树优化DP】

$m$的数据范围看起来非常有问题??仔细多列几个例子可以发现,在$m<=5$的时候,只要找到有两行状态按位$&$起来等于$0$,就是可行方案,如果没有就不行. #include<iostream> #include<cstdio> #include<cstring> using namespace std; int cnt[1<<4+1], n, m; int main ( ) { freopen ( "prob.in",

理想乡题解 (线段树优化dp)

题面 思路概述 首先,不难想到本题可以用动态规划来解,这里就省略是如何想到动态规划的了. 转移方程 f[i]=min(f[j]+1)(max(i-m,0)<=j<i 且j符合士兵限定) 注意要用 max(i-m,0)以防止越界 我们先用两个数组sl,sa分别统计1~i个士兵中有多少个Lencer和Archer 然后在max(i-m,0)中寻找符合条件的j (1).两种士兵相差不超过k. 这个好说abs((sl[i]-sl[j])-(sa[i]-sa[j]))<=k 不要忘了第二种情况!!

hdu45221——小明系列问题——小明序列 线段树优化dp

小明系列问题--小明序列 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 1918    Accepted Submission(s): 583 Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来找