zoukankan      html  css  js  c++  java
  • 【BZOJ1835】基站选址(ZJOI2010)-DP+线段树优化

    测试地址:基站选址
    题目大意:n(20000)个村庄,第i个村庄坐标为di。现在要在村庄中建造不超过k(100)个基站,在村庄i建造基站的费用是ci,并且如果没有在村庄i周围si距离的范围内建造基站的话,就要支付Wi的补偿费。求最小花费。
    做法:本题需要用到DP+线段树优化。
    首先,很容易想到区间DP的形式。令f(i,j)为只考虑前i个村庄,在第i个村庄建造基站,在前i个村庄共建造j个基站的最小花费。可以得到状态转移方程:
    f(i,j)=min{f(k,j1)+cost(k,i)}+ci
    其中cost(k,i)指第k到第i个村庄中,如果除了第k和第i个选择外都不选,所需要花费的补偿费。如果我们多设一个空点n+1,那么答案就是min{f(n+1,x)}(1xk+1)
    这个方程的瓶颈在于cost的计算,直接暴力算肯定会炸,于是我们需要思考一个新的方法。
    注意到对于每个村庄,可以覆盖它的建造基站地点是一个区间[li,ri],不难看出这个区间会对cost(l,r)(l<li,r>ri)做出贡献。按照DP的过程,r会不断递增,那么这就是一个自然的扫描线了,于是我们只需要在处理r时,把所有区间[li,ri](ri=r1)的贡献累计到cost数组中即可,发现这是一个区间加的操作,于是用线段树维护,转移的时候在线段树中求出f(k,j1)+cost(k,i)的最小值即可。
    于是我们就以O(knlogn)的时间复杂度完成了这一题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll inf=1000000000ll*1000000000ll;
    int n,k,d[20010],s[20010];
    int st[20010]={0},ed[20010]={0};
    ll c[20010]={0},w[20010],f[20010][2]={0};
    ll seg[80010],tag[80010];
    int now=0,past=1;
    struct interval
    {
        int l,r;
        ll w;
    }t[20010];
    
    void pushdown(int no)
    {
        if (tag[no]!=0)
        {
            seg[no<<1]+=tag[no],seg[no<<1|1]+=tag[no];
            tag[no<<1]+=tag[no],tag[no<<1|1]+=tag[no];
            tag[no]=0;
        }
    }
    
    void pushup(int no)
    {
        seg[no]=min(seg[no<<1],seg[no<<1|1]);
    }
    
    void buildtree(int no,int l,int r)
    {
        tag[no]=0;
        if (l==r)
        {
            seg[no]=f[l][past];
            return;
        }
        int mid=(l+r)>>1;
        buildtree(no<<1,l,mid);
        buildtree(no<<1|1,mid+1,r);
        pushup(no);
    }
    
    void modify(int no,int l,int r,int s,int t,ll x)
    {
        if (l>=s&&r<=t)
        {
            seg[no]+=x;
            tag[no]+=x;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(no);
        if (s<=mid) modify(no<<1,l,mid,s,t,x);
        if (t>mid) modify(no<<1|1,mid+1,r,s,t,x);
        pushup(no);
    }
    
    ll query(int no,int l,int r,int s,int t)
    {
        if (s>t) return 0;
        if (l>=s&&r<=t) return seg[no];
        int mid=(l+r)>>1;
        ll ans=inf;
        pushdown(no);
        if (s<=mid) ans=min(ans,query(no<<1,l,mid,s,t));
        if (t>mid) ans=min(ans,query(no<<1|1,mid+1,r,s,t));
        return ans;
    }
    
    bool cmp(interval a,interval b)
    {
        return a.r<b.r;
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        d[1]=0;
        for(int i=2;i<=n;i++)
            scanf("%d",&d[i]);
        for(int i=1;i<=n;i++)
            scanf("%lld",&c[i]);
        for(int i=1;i<=n;i++)
            scanf("%d",&s[i]);
        for(int i=1;i<=n;i++)
            scanf("%lld",&w[i]);
    
        for(int i=1;i<=n;i++)
        {
            t[i].w=w[i];
            int l,r;
            l=1,r=i;
            while(l<r)
            {
                int mid=(l+r)>>1;
                if (d[mid]>=d[i]-s[i]) r=mid;
                else l=mid+1;
            }
            t[i].l=l;
            l=i,r=n;
            while(l<r)
            {
                int mid=(l+r)>>1;
                if (d[mid+1]<=d[i]+s[i]) l=mid+1;
                else r=mid;
            }
            t[i].r=l;
        }
        sort(t+1,t+n+1,cmp);
        for(int i=1;i<=n;i++)
            if (!st[t[i].r])
            {
                st[t[i].r]=i;
                if (i>1) ed[t[i-1].r]=i-1;
            }
        ed[t[n].r]=n;
    
        for(int i=1;i<=n;i++)
            f[i][past]=inf;
        ll ans=f[n][past];
        for(int i=1;i<=k+1;i++)
        {
            buildtree(1,0,n);
            for(int j=1;j<=n+1;j++)
            {
                if (st[j-1])
                {
                    for(int p=st[j-1];p<=ed[j-1];p++)
                        modify(1,0,n,0,t[p].l-1,t[p].w);
                }
                f[j][now]=query(1,0,n,0,j-1)+c[j];
            }
            ans=min(ans,f[n+1][now]);
            swap(now,past);
        }
        printf("%lld",ans);
    
        return 0; 
    }
  • 相关阅读:
    Redis在Windows上使用和集群配置
    Lzma(7-zip)和zlib
    Windump教程-参数介绍
    Windows 使用windump进行循环抓包
    wireshark长时间抓包分多个文件
    发现TCP的一种错误----客户端连接失败(10055错误号)
    MySQL [Err] 1055
    解决socket交互的10048和10055错误的总结
    Socket调用Close后如何终止套接口的问题
    linux下recv 、send阻塞、非阻塞区别和用法
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793267.html
Copyright © 2011-2022 走看看