CodeForces 115E Linear Kingdom Races

CodeForces115E Linear Kingdom Races

题目大意:

给你一段区间 [1,n] [ 1 , n ] ,区间上的每一个点都有一个使用代价
给你一些小区间 当你使用这些小区间时 可以获得报酬
但是要先付出区间上每一个点的代价(每个点的代价只需要付一次就可以多次使用)
问 最大利润是多少

据说是分治题 借鉴了前辈们的题解之后 觉得做法很巧妙

首先说一下这道题目的难以处理的地方吧

多段区间可能会有交集 存在一些情况 是的两个区间单独选择时都是亏本的 但是一起选择时就有利润
因此我们就不能够单独地看某一个区间是否要使用(因为单独看是亏本的 要一起看才能够获利)

解决方法:线段树

不按照一整个一整个的区间进行规划
而是按照右端点的位置进行规划

对于一个区间 [L,R] [ L , R ] 它的决定因素应该是 L1 L − 1 的值
因为有 DP[1.....L,R]=DP[1.....L1]+GET[L,R] D P [ 1..... L , R ] = D P [ 1..... L − 1 ] + G E T [ L , R ]
那么 如果对于当前区间 我们把当前点 R R (按照右端点进行规划)的花费加到 [1R1] 上,然后把收入加到 L1 L − 1

统计时统计加上这些花费和收入之后的 [1,L1] [ 1 , L − 1 ] 的最大值 和 DP[R1] D P [ R − 1 ] 的做比较
如果 统计得到的最大值更大 说明选择某一些以 R R 为右端点的区间 就可以得到更优解
反之 如果 DP[R1] 的值更大 说明不选择 R R 和以 R 为右端点的区间更优

然后将统计出来的最大值加到 R R 点上 作为往后面统计的基础

为什么可行

就按照单独一个区间来看
我们将 [L,R] 的花费全部都加到了对应的 [1,P1] [ 1 , P − 1 ] 上,因此在统计时, L1 L − 1 上是一定减去了 [L,R] [ L , R ] 上的花费的
同样的 收入也被加入了上去
因此我们统计时对应的区间是被相应的改动了的

那么对于统计出来的 R R 点的值,就是最优的,并且因为 R 点的值一定是相比于 R1 R − 1 更优的,所以说在之后的统计, R R 在统计是一定是比 R1 更优的,因此,之前在 [1,R1] [ 1 , R − 1 ] 减去的花费/加上的收入 都不会影响到后面的,而此时的 R R 点一定是选择了 [L,R] 之间最优解得到的

并且,因为只有修改操作,并且每次修改的区间都是 [1,L] [ 1 , L ] ,所以说其实单次修改最大操作复杂度就是 O(logn) O ( l o g n )

为什么非得修改完[1,P-1]

因为你不知道 以 R R 为右端点的区间的左端点到底是啥,而修改的时候涉及到的点是 [L,R] 之间的全部点,所以说必须将值修改到 L1 L − 1

放参考代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#define mid (l+r>>1)
#define ls (o<<1)
#define rs (o<<1|1)
#define getchar() (*inp++)
using namespace std;

char INP[1<<25],*inp=INP;

inline int input()
{
    char c=getchar();int o;bool f=0;
    while(c>57||c<48)f|=(c=='-'),c=getchar();
    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
    return f?-o:o;
}

struct node{int l,r;long long c;}race[200123];
bool cmp(node A,node B)
{
    if(A.r!=B.r)return A.r<B.r;
    return A.l<B.l;
}

long long mx[800123],wait[800123];
int L,R;long long V;

void PD(int o)
{
    long long wo=wait[o];
    wait[ls]+=wo;wait[rs]+=wo;
    mx[ls]+=wo;mx[rs]+=wo;
    wait[o]=0;
}

void add(int o,int l,int r)
{
    if(L<=l&&r<=R)
    {
        wait[o]+=V;
        mx[o]+=V;
        return;
    }
    PD(o);
    if(L<=mid&&l<=R)add(ls,l,mid);
    if(L<=r&&mid<R)add(rs,mid+1,r);
    mx[o]=max(mx[ls],mx[rs]);
}

long long DP[200123],cost[200123];

int main()
{
    //freopen("In.txt","r",stdin);
    fread(INP,1,1<<25,stdin);
    int n=input(),m=input();
    for(int i=1;i<=n;i++)cost[i]=input();
    for(int i=1;i<=m;i++)
    {
        race[i].l=input();
        race[i].r=input();
        race[i].c=input();
    }
    sort(race+1,race+m+1,cmp);
    int cnt=1;
    for(int i=1;i<=n;i++)
    {
        L=0;R=i-1;V=-cost[i];
        add(1,0,n);
        while(race[cnt].r==i)
        {
            R=race[cnt].l-1;V=race[cnt].c;
            add(1,0,n);
            cnt++;
        }
        L=R=i;
        V=DP[i]=max(DP[i-1],mx[1]);
    add(1,0,n);
    }
    printf("%I64d",DP[n]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值