zoukankan      html  css  js  c++  java
  • [ZJOI2010]基站选址,线段树优化DP

    G. base 基站选址

    内存限制:128 MiB 时间限制:2000 ms 标准输入输出
    题目类型:传统 评测方式:文本比较
     

    题目描述

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

    输入格式

    输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN。

    输出格式

    输出文件中仅包含一个整数,表示最小的总费用。

    样例

    样例输入

    3 2 1 2 2 3 2 1 1 0 10 20 30
    

    样例输出

    4
    

    数据范围与提示

    40%的数据中,N<=500; 100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。


     1.lazy标记打错了,应该是 += ,不能直接覆盖
        2.lc<<1 打成了 >>1 ,调了半天
        3.优化思路:首先是每个村庄的左右端点,二分查找并记录
        DP方程 f[i][j]=min(f[k][j-1]+pay[x])+c[i],表示第 j 个基站建在第 i 个村庄时的最小花费,不考虑 i+1 到 n
        显然,可以去掉一维
        f[i]=min(f[i]+pay[x])+c[i];
        此时,考虑优化计算 pay[x] ,也就是没有被覆盖的点的花费
        因为若 ed[x]=i,此时 i 不被选择,那么 计算 i+1 时就要加上花费
        所以,我们应维护一个区间,存储上一个基站建在 i 时的最小花费之和 ,即 f[k][j-1]+pay[x];
        在计算时,查找 [1,i-1]的最小值
        然后更新所有以 i 为右端点的村庄的花费,区间为 [1,st[x]-1];
        最后,因为我们考虑的是当前点对前面的影响,所以我们在最后新建一个假点,用来保存结果
      个人总结:把线段树当成一种工具,可以快速存储,查找想要的值,利用这一特性我们有规律地存储我们想要的值。可达到大大节约时间的目的

    代码:

    #include<bits/stdc++.h>
    #define re register int
    #define int long long
    #define lc (p<<1)
    #define rc (p<<1|1)
    using namespace std;
    const int N=2e4+10;
    const int INF=1e12+7;
    int n,k,tot,num;
    int dis[N],c[N],s[N],w[N],st[N],ed[N];
    struct TREE
    {
        int zh,lazy;
    }use[N*40];
    long long f[N];
    int to[N<<1],next[N<<1],head[N<<1];
    void add(int x,int y)
    {
        to[++tot]=y;
        next[tot]=head[x];
        head[x]=tot;
    }
    void pp(int p)
    {
        use[p].zh=min(use[lc].zh,use[rc].zh);
    }
    void pd(int p)
    {
        if(!use[p].lazy)
            return;
        use[lc].lazy+=use[p].lazy;
        use[rc].lazy+=use[p].lazy;
        use[lc].zh+=use[p].lazy;
        use[rc].zh+=use[p].lazy;
        use[p].lazy=0;
    }
    void build(int p,int l,int r)
    {
        use[p].lazy=0;
        if(l==r)
        {
            use[p].zh=f[l];
            return;
        }
        int mid=(l+r)>>1;
        build(lc,l,mid);
        build(rc,mid+1,r);
        pp(p);
    }
    int query(int p,int L,int R,int l,int r)
    {
        if(l>r)
            return INF;
        if(l<=L&&R<=r)
            return use[p].zh;
        int mid=(L+R)>>1;
        int ans=INF;
        pd(p);
        if(l<=mid)
            ans=min(ans,query(lc,L,mid,l,r));
        if(mid<r)
            ans=min(ans,query(rc,mid+1,R,l,r));
        return ans;
    }
    void change(int p,int L,int R,int l,int r,int z)
    {
        if(l>r)
            return;
        if(l<=L&&R<=r)
        {
            use[p].zh+=z;
            use[p].lazy+=z;
            return;
        }
        int mid=(L+R)>>1;
        pd(p);
        if(l<=mid)
            change(lc,L,mid,l,r,z);
        if(mid<r)
            change(rc,mid+1,R,l,r,z);
        pp(p);
    }
    signed main()
    {
        scanf("%lld%lld",&n,&k);
        for(re i=2;i<=n;i++)
            scanf("%lld",&dis[i]);
        for(re i=1;i<=n;i++)
            scanf("%lld",&c[i]);
        for(re i=1;i<=n;i++)
            scanf("%lld",&s[i]);
        for(re i=1;i<=n;i++)
            scanf("%lld",&w[i]);
        ++n;
        ++k;
        dis[n]=INF;
        w[n]=INF;
        for(re i=1;i<=n;i++)
        {
            st[i]=lower_bound(dis+1,dis+n+1,max(dis[i]-s[i],0*1ll))-dis;
            ed[i]=upper_bound(dis+1,dis+n+1,min(dis[i]+s[i],dis[n]))-dis;
            ed[i]-=1;
            add(ed[i],i);    
        }
        int sum=0;
        for(re i=1;i<=n;i++)
        {
            f[i]=sum+c[i];
            for(re j=head[i];j;j=next[j])
                sum+=w[to[j]];
        }
        int out=f[n];
        for(re i=2;i<=k;i++)
        {
            build(1,1,n);
            for(re j=1;j<=n;j++)
            {
                f[j]=query(1,1,n,1,j-1)+c[j];
                for(re k=head[j];k;k=next[k])
                {
                    int p=to[k];
                    change(1,1,n,1,st[p]-1,w[p]);
                }
            }
            out=min(out,f[n]);
        }
        printf("%lld\n",out);
    }
  • 相关阅读:
    使用Xshell和Xftp部署简单的项目
    1-27 sed基本编程和cut基本应用
    1-26-1-expect无交互式-正则表达式
    1-24-case流程控制和while循环语句的使用
    1-23-shell脚本之-if流程控制语句和for循环语句的使用
    1-22-shell脚本基本应用-实验手册
    1-22-shell脚本的基础
    1-21 网络管理相关的命令
    总结 1-1 ~ 1-6
    windows系统下Eclipse启动界面更改
  • 原文地址:https://www.cnblogs.com/WindZR/p/14866067.html
Copyright © 2011-2022 走看看