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);
    }
  • 相关阅读:
    UVa 1354 天平难题 (枚举二叉树)
    广西邀请赛总结
    UVa 12118 检查员的难题 (dfs判连通, 构造欧拉通路)
    UVA
    Uva 127 "Accordian" Patience (模拟)
    UVA 10539 Almost Prime Numbers( 素数因子)
    HDU 1272 小希的迷宫(并查集)
    HDU 1213 How Many Tables (并查集)
    POJ 2236 Wireless Network(并查集)
    HDU 1233 还是畅通工程 ( Kruskal或Prim)
  • 原文地址:https://www.cnblogs.com/WindZR/p/14866067.html
Copyright © 2011-2022 走看看