jzoj4646. 【NOI2016模拟7.16】建造记者站

32 篇文章 0 订阅
14 篇文章 0 订阅

题目描述

这里写图片描述
这里写图片描述

挺水的一道DP

10%

直接设f[i][j]表示
当前到第i个村庄,已经建了j个记者站
于是
f[i][j]=max(f[k][j1]+cost(k,i)) f [ i ] [ j ] = m a x ( f [ k ] [ j − 1 ] + c o s t ( k , i ) )
其中cost(x,y)表示当x、y建立记者站,xy中间不建时x~y村庄的额外花费
然后暴力算cost
复杂度: O(n3m) O ( n 3 m )

30%

预处理一下cost
不会

100%

然而我只会100分
通过观察发现,当i确定后,实际就是对一段区间最小值的查询。
显然就是线段树

重点是怎么算


首先,从i往前的cost是单调不减的(废话)

一个点未被覆盖,前提是 该点的左半部分和右半部分都没有记者站。
这里写图片描述
因为i是递增的,所以可以把每个点范围的右边界排序。之后按顺序扫i,就可以处理右半边。
当一个点j的右半边没有记者站时,在j左边界以左的点就要加上j的额外代价

因为坐标范围是 109 10 9 ,然后用坐标来建线段树也比较蛋疼
所以直接按编号来建线段树
先求出在j左边界左边且最靠近点i左边界的点left[j]
之后每次区间修改1~left[j]

最后求出每个点到n(n不选)的费用
这和求left一样可以直接排序线性

code

169行……真短啊 ( ̄▽ ̄)”

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
using namespace std;

struct AA{long long x,y;};

AA a[20001];
long long D[20001];
long long C[20001];
long long R[20001];
long long P[20001];
long long LF[20001];
long long RT[20001];
long long f[101][20001];
long long tr[80002][2];
long long n,m,i,j,k,l,ans,I;

bool cmp(AA a,AA b) {return a.x<b.x;}

long long cost(long long l,long long r)
{
    long long i,ans=0;

    fo(i,l,r)
    if (D[i]-R[i]>D[l] && D[i]+R[i]<D[r])
    ans+=P[i];

    return ans;
}

void down(long long t,bool type)
{
    tr[t][0]+=tr[t][1];
    if (type)
    {
        tr[t*2][1]+=tr[t][1];
        tr[t*2+1][1]+=tr[t][1];
    }
    tr[t][1]=0;
}

void maketree(long long t,long long l,long long r)
{
    long long mid=(l+r)/2;

    tr[t][1]=0;
    if (l==r)
    {
        tr[t][0]=f[i-1][l];
        return;
    }

    maketree(t*2,l,mid);
    maketree(t*2+1,mid+1,r);

    tr[t][0]=min(tr[t*2][0],tr[t*2+1][0]);
}

void change(long long t,long long l,long long r,long long x,long long y,long long s)
{
    long long mid=(l+r)/2;

    if (x<=l && r<=y)
    {
        tr[t][1]+=s,down(t,l!=r);
        return;
    }
    down(t,1);

    if (x<=mid) change(t*2,l,mid,x,y,s);
    if (mid<y)  change(t*2+1,mid+1,r,x,y,s);

    tr[t][0]=min(tr[t*2][0]+tr[t*2][1],tr[t*2+1][0]+tr[t*2+1][1]);
}

long long find(long long t,long long l,long long r,long long x,long long y)
{
    long long mid=(l+r)/2;

    down(t,l!=r);
    if (x<=l && r<=y)
    return tr[t][0];

    long long Ans,ans=2033333333;
    if (x<=mid)
    Ans=find(t*2,l,mid,x,y),ans=min(ans,Ans);
    if (mid<y)
    Ans=find(t*2+1,mid+1,r,x,y),ans=min(ans,Ans);

    return ans;
}

int main()
{
    freopen("jhaha.in","r",stdin);
    freopen("jhaha.out","w",stdout);

    memset(f,127,sizeof(f));

    scanf("%d%d",&n,&m);
    fo(i,2,n) scanf("%d",&D[i]);
    fo(i,1,n) scanf("%d",&C[i]);
    fo(i,1,n) scanf("%d",&R[i]);
    fo(i,1,n) scanf("%d",&P[i]);

    D[0]=-2033333333;
    D[n+1]=2033333333;

    fo(i,1,n) a[i].x=D[i]-R[i],a[i].y=i;
    sort(a+1,a+n+1,cmp);
    j=0;
    fo(i,1,n)
    {
        while (D[j+1]<a[i].x) j++;
        LF[a[i].y]=j;
    }
    j=n+1;k=0;
    fd(i,n,0)
    {
        while (j && a[j-1].x>D[i]) j--,k+=P[a[j].y];
        RT[i]=k;
    }

    fo(i,1,n) a[i].x=D[i]+R[i],a[i].y=i;
    sort(a+1,a+n+1,cmp);

    ans=233333333;
    f[0][0]=0;
    fo(i,1,m)
    {
        I=1;
        maketree(1,0,n);

        fo(j,1,n)
        {
            while (I<=n && D[j]>a[I].x)
            {
                change(1,0,n,0,LF[a[I].y],P[a[I].y]);
                I++;
            }

            f[i][j]=find(1,0,n,0,j-1);
            f[i][j]+=C[j];
        }
    }

    fo(i,0,m)
    {
        fo(j,i,n)
        if (f[i][j]<2139062143)
        {
            k=RT[j];
            ans=min(ans,f[i][j]+k);
        }
    }

    printf("%d\n",ans);

    fclose(stdin);
    fclose(stdout);

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值