bzoj1835: [ZJOI2010]base 基站选址

Posted AKCqhzdy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1835: [ZJOI2010]base 基站选址相关的知识,希望对你有一定的参考价值。

新的一年新的开始。结果第一题就用了几乎一周。而且感觉很不好。

先检讨自己。最近写的各种数据结构模板基本没打过出来,各种细节崩盘,这题线段树都居然被lazy标记没清零卡挂。

DP还是博大精深,这东西感觉没学好啊。

---------------------------------------

很容易想DP的。主要是怎么D,一开始我的想法是f[i][j]表示到第1~i个点被覆盖,建了j个站。但是这题的覆盖是关于当前点的,什么意思,就是可能k1点在k2点前面,但k1受到了很后面的k3覆盖不用补偿,而k2却要,这样就gg。

那改一下,f[i][j]表示在第i个点建站,建了j个。DP方程就是这样的。

f[i][j]=max(f[i][j],f[k][j-1]+(k~x中没被覆盖的点的w和))+c[i];

而这个时间复杂度O(n^2*k)必挂。

膜了题解才发现,这题是一道经典的线段树维护DP,线段树负责区间修改和区间查询。时间复杂度就可以降到O(nlogn*k)

显然维护的就是f[k][j]+(k~x中没被覆盖的点的w和),按照1~k下标即可。

那么难点在于(k~x中没被覆盖的点的w和)

考虑四个点k,p,x,x+1

k表示被继承点,x、x+1可以继承k(当然还有其他,方便起见就不罗列了)

那么对于k和x,若x继承k,那么k、x之间肯定没有点建站,那么f[k][j-1]+(k~x中没被覆盖的点的w和)假设已经预处理了,再假设x这个站建下来,x覆盖了p,并且p和x的距离就是p能被影响的最远距离

然后对于x+1的话,如果x+1要继承k,那么p必定没有覆盖,因为x已经是最远的点了,x+1无论如何也覆盖不了。

由此,我们二分预处理出一个点往后最远可以影响到它的点,然后用邻接表把最远距离相等的点记录,那么当枚举x到x+1的时候,就可以快速把那些与p情况一样的点的w加上就行了。

那么还有一个问题,k也可以覆盖到后面的点呀,假如x+1覆盖不了,但k覆盖得了,那也不应该把w加上。

解决方法是再二分预处理出一个点往前最远可以影响到它的点,当区间修改的时候,就修改那些在它前面的,影响不到它的点,这些点就是1~L[x]-1

 

PS:实际的操作中j没必要留一维。当j-1的信息记入线段树中时,f就没用了,直接在上面更新

还有就是n++,m++,在最后多弄一个d=inf,c=0,s=0,的结束点,由于s=0,所以m++的那个站肯定建在n+1位置,又由于d=inf,不影响其他原来的点,此举旨在将最后答案确定到n+1位置方便取最大,因为对于这个问题,建站不一定要建在n位置,同时判断前面的位置能否覆盖后面也很麻烦。

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf=2147483647;

int n,m;
int d[21000],c[21000],w[21000];
int L[21000],R[21000];//表示最远可以影响到当前点的位置 
LL f[21000];

struct line
{
    int to,next;
}l[21000];int llen,last[21000];
void ins(int x,int y)
{
    llen++;l[llen].to=y;
    l[llen].next=last[x];last[x]=llen;
}

int fhj(int k)
{
    int l=1,r=n,ret;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(d[mid]>=k)r=mid-1, ret=mid;
        else l=mid+1;
    }
    return ret;
}
int fqq(int k)
{
    int l=1,r=n,ret;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(k>=d[mid])l=mid+1, ret=mid;
        else r=mid-1;
    }
    return ret;
}
void sc()
{
    scanf("%d%d",&n,&m);n++;m++;
    llen=0;memset(last,0,sizeof(last));

    d[1]=0;
    for(int i=2;i<n;i++)scanf("%d",&d[i]);
    d[n]=inf;
    for(int i=1;i<n;i++)scanf("%d",&c[i]);
    c[n]=0;
    int x;
    for(int i=1;i<n;i++)
        scanf("%d",&x), L[i]=fhj(d[i]-x),  R[i]=fqq(d[i]+x), ins(R[i],i);
    L[n]=n;R[n]=n;
    for(int i=1;i<n;i++)scanf("%d",&w[i]);
    
}

//----------sc----------------------

struct seq
{
    int l,r,lc,rc;LL c,lazy;
}tr[41000];int trlen;
void bt(int l,int r)
{
    trlen++;int now=trlen;
    tr[now].l=l;tr[now].r=r;
    tr[now].c=0;tr[now].lazy=0;
    tr[now].lc=tr[now].rc=-1;
    if(l<r)
    {
        int mid=(l+r)/2;
        tr[now].lc=trlen+1;bt(l,mid);
        tr[now].rc=trlen+1;bt(mid+1,r);
        tr[now].c=min(tr[tr[now].lc].c,tr[tr[now].rc].c);
    }
    else tr[now].c=f[l];
}
LL findmin(int now,int l,int r)
{
    if(tr[now].l==l&&tr[now].r==r){return tr[now].c;}
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    
    if(tr[now].lazy!=0)
    {
        tr[lc].c+=tr[now].lazy;
        tr[rc].c+=tr[now].lazy;
        tr[lc].lazy+=tr[now].lazy;
        tr[rc].lazy+=tr[now].lazy;
        tr[now].lazy=0;
    }
        
    if(r<=mid)return findmin(lc,l,r);
    else if(mid+1<=l)return findmin(rc,l,r);
    else return min(findmin(lc,l,mid),findmin(rc,mid+1,r));
}
void change(int now,int l,int r,int k)
{
    if(l>r)return ;
    if(tr[now].l==l&&tr[now].r==r){tr[now].c+=k;tr[now].lazy+=k;return ;}
    int lc=tr[now].lc,rc=tr[now].rc;
    int mid=(tr[now].l+tr[now].r)/2;
    
    if(tr[now].lazy!=0)
    {
        tr[lc].c+=tr[now].lazy;
        tr[rc].c+=tr[now].lazy;
        tr[lc].lazy+=tr[now].lazy;
        tr[rc].lazy+=tr[now].lazy;
        tr[now].lazy=0;
    }
    
    if(r<=mid)change(lc,l,r,k);
    else if(mid+1<=l)change(rc,l,r,k);
    else change(lc,l,mid,k), change(rc,mid+1,r,k);
    
    tr[now].c=min(tr[lc].c,tr[rc].c);
}

//-------seq----------------

int main()
{
    freopen("base.in","r",stdin);
    freopen("base.out","w",stdout);
    sc();
    
    int tre=0;
    for(int i=1;i<=n;i++)
    {
        f[i]=tre+c[i];
        for(int k=last[i];k;k=l[k].next)
        {
            int to=l[k].to;
            tre+=w[to];
        }
    }
    //yu j=1
    LL ans=f[n];
    for(int j=2;j<=m;j++)
    {
        trlen=0;bt(1,n);
        for(int i=j;i<=n;i++)
        {
            f[i]=findmin(1,j-1,i-1)+c[i];
            for(int k=last[i];k;k=l[k].next)
            {
                int to=l[k].to;
                change(1,1,L[to]-1,w[to]);
            }
        }
        ans=min(ans,f[n]);
    }
    printf("%lld\n",ans);
    return 0;
}

 

以上是关于bzoj1835: [ZJOI2010]base 基站选址的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1835: [ZJOI2010]base 基站选址

BZOJ 1835 [ZJOI2010]base 基站选址

bzoj1835[ZJOI2010]base基站选址

bzoj1835 [ZJOI2010]基站选址

[BZOJ1835][ZJOI2010]base 基站选址(DP+线段树)

bzoj 1835 base 基站选址 - 动态规划 - 线段树