FZU 2079 最大获利(线段树+DP)

Description

Sean准备投资一些项目。有n个投资项目,投资第i个项目需要花费Ci元。Sean发现如果投资了某些编号连续的项目就能赚得一定的钱。现在给出m组连续的项目和每组能赚得的钱,请问采取最优的投资策略的最大获利是多少?

样例最佳策略是全部项目都投资,然后第1,2组都满足了,获利为2+2-3=1。最佳策略可能是不投资,即最大获利为0。

Input

每组数据第一行输入两个整数N和M , N表示项目数,M表示能获利的连续的项目组数,1 <= N <= 20000,1 <= M <= 20000 , 接下来一行输入N个数,表示每个项目投资需要的花费Ci,1<=Ci<=10^9。

接下来m行,每行3个数Li,Ri,Pi,1<=Li<=Ri<=N,1<=Pi<=10^9,表示投资从第Li到第Ri这些连续的项目的获利。每组连续投资的获利互不影响,如果投资的多组连续投资都包含项目i,项目i只需要投资一次。

Output

对于每组数据输出一行,即最大获利。

Sample Input

3 2

1 1 1

1 2 2

2 3 2

Sample Output

1

设dp[i]表示,前i个项目的最大获利。

转移方程 : dp[i] = max( dp[i-1] , dp[j-1] + w[j][i] ) , ( 1<=j <=n ).

枚举状态费用O(n) , 用线段树优化取最值,更新费用为O(log n ) , 总复杂度为O(n log n )。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N = 200010;
const int inf = 1e9+7;
const double PI = acos(-1.0);
const double eps = 1e-6 ;

#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lr rt<<1
#define rr rt<<1|1

int n , m ;
struct node {
    int l , r , w ;
    bool operator < ( const node &a ) const {
        if( r != a.r ) return r < a.r ;
        else return l > a.l ;
    }
}e[N];

LL dp[N] , cost[N] ;

void init(){
    memset( dp , 0 , sizeof dp );
}

LL date[N<<2] ,lazy[N<<2];

void build( int l , int r , int rt ) {
    date[rt] = lazy[rt] = 0 ;
    if( l == r ) return ;
    int mid = ( l + r ) >> 1 ;
    build(lson); build(rson);
}

void Down( int l , int r , int rt ) {
    if( l == r ) return  ;
    if( lazy[rt] != 0 ) {
        date[lr]+= lazy[rt] , lazy[lr] += lazy[rt];
        date[rr]+= lazy[rt] , lazy[rr] += lazy[rt];
        lazy[rt] = 0 ;
    }
}

void Up( int rt ) { date[rt] = max( date[lr] , date[rr] ); }

void update( int l , int r , int rt , int L , int R , LL val) {

    if( L == l && r == R ) {
        date[rt] += val , lazy[rt] += val ; return ;
    }
    Down(l,r,rt);
    int mid = (l+r) >> 1;
    if( R <= mid ) update(lson,L,R,val);
    else if( L > mid ) update(rson,L,R,val);
    else update(lson,L,mid,val) , update(rson,mid+1,R,val);
    Up(rt);
}

LL query( int l , int r , int rt , int L , int R ) {

    if( L == l && r == R ) {
        return date[rt];
    }
    Down(l,r,rt);
    int mid = (l+r)>>1;
    if( R <= mid ) return query(lson,L,R);
    else if( L > mid ) return query(rson,L,R);
    else return max( query(lson,L,mid),query(rson,mid+1,R) );
}

void run () {
    init() ;
    build(root);
    for( int i = 1 ; i <= n ; ++i ){
        scanf("%I64d",&cost[i]) ;
    }
    for( int i = 0 ; i < m ; ++i ) {
        scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].w);
    }
    sort( e , e + m );
    int head = 0 ;
    for( int i = 1 ; i <= n ; ++i ) {
        update( root , i  , i  , dp[i-1] );
        update( root , 1 , i , -cost[i] );
        while( head < m && e[head].r <= i )
            update( root , 1 , e[head].l , e[head].w ) , head++ ;
        dp[i] = max( dp[i-1] , query( root , 1 , i ) );
    }
    printf("%I64d\n",dp[n]);
}
int main()
{
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif // LOCAL
    ios::sync_with_stdio(false);
    while( ~scanf("%d%d",&n,&m) )run() ;
}

时间: 2024-08-29 15:43:06

FZU 2079 最大获利(线段树+DP)的相关文章

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu

HDU6447 YJJ&#39;s Salesman 2018中国大学生程序设计竞赛 - 网络选拔赛1010 离散化+线段树+DP

YJJ's Salesman Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 253    Accepted Submission(s): 62 Problem Description YJJ is a salesman who has traveled through western country. YJJ is always on

[HDU 6447][YJJ&#39;s Salesman][2018CCPC网络选拔赛 1010][离散化+线段树+DP]

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6447 题意: 左上角(0,0),右下角(10^9,10^9)的网格,其中有n(1<=n<=10^5)个方格内有权值. 一次只能沿右,下,右下三个方向走一个格子,只有沿右下方向走到格子里才可以获得权值. 问从(0,0)到(10^9,10^9)的路径最大权值是多少. 思路: 网格路径权值问题,第一感考虑DP,x从上往下,y从左往右刷表,状态转移方程为dp[i][j]=max(dp[i-1][j],dp[

CF932F(李超线段树+dp)

CF932F(李超线段树+dp) 此题又是新玩法, 李超线段树合并优化\(dp\) 一个显然的\(\Theta(n^2)dp\): \(dp[x]\)表示从x出发到叶子节点的最小代价 \(dp[x] = \min(dp[y] + a[x] * b[y]) ~~(y \in subtree(x))\) 如果我们将\(b[y]\)看成斜率, \(dp[y]\)看成纵截距, \(a[x]\)看成横坐标, 那么问题转为了在平面上有一些直线, 选出与直线\(x = a[x]\)相交的最靠下的点吗, 李超线

FZu Problem 2236 第十四个目标 (线段树 + dp)

题目链接: FZu  Problem 2236 第十四个目标 题目描述: 给出一个n个数的序列,问这个序列内严格递增序列有多少个?不要求连续 解题思路: 又遇到了用线段树来优化dp的题目,线段树节点里面保存所表达区间里面的方案数.先离散化序列(升序排列),建树,然后按照没有sort前的顺序向线段树里面加点,每次查询小于该数的方案数目+1, 就是当前节点插入进去能影响的方案数目.在线段树对应位置加上新增加的数目即可. 1 #include <cstdio> 2 #include <queu

【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

[BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述. 第

hdu4521(线段树+dp)

传送门:小明系列问题——小明序列 题意:有n个数,求间距大于d的最长上升序列. 分析:dp[i]表示在i点以a[i]结束距离大于d的最长上升序列,然后每更新到第i点时,取i-d之前小于a[i]的数为结束的最长上升序列进行状态转移,并维护前i-d之前的最大上升序列,维护i-d之前的每点为结束的最长上升序列用线段树维护即可. #pragma comment(linker,"/STACK:1024000000,1024000000") #include <cstdio> #inc

lightoj1085 线段树+dp

1 //Accepted 7552 KB 844 ms 2 //dp[i]=sum(dp[j])+1 j<i && a[j]<a[i] 3 //可以用线段树求所用小于a[i]的dp[j]的和 4 //需要离散化 5 #include <cstdio> 6 #include <cstring> 7 #include <iostream> 8 #include <queue> 9 #include <cmath> 10 #