51nod 1611 金牌赛事(动态规划 + 线段树)

分析:看起来有点像最大权闭合图,然而复杂度太高。。。

正解是dp,设dp[i]为考虑前i条路的最大收益,则dp[i]=max{dp[j] - cost[j+1][i] + earn[j+1][i]},0<=j<=i-1,earn[j+1][i]表示在[j+1,i]之间的比赛,是个O(n^2)的dp.

接下来考虑优化这个dp.维护一个线段树,当扫到i时,树中满足a[j] = dp[j] - cost[j+1][i] + earn[j+1][i],先将比赛按右端点排序,然后每扫到一个右端点为i的比赛,将所有j<l的a[j] 加上earn,将所有j<i的a[i]减去c[i],然后更新dp[i]为[0,i-1]的最大值。复杂度为O(nlogn)。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cstdio>
  5 using namespace std;
  6 typedef long long ll;
  7 const int maxn=2e5+5;
  8 const ll inf = 1e18;
  9 struct Contest{
 10     int l,r;
 11     ll earn;
 12 }p[maxn];
 13 int n, m;
 14 //bool Cmp(Pair a,Pair b){return a.r-a.l<b.r-b.l;}
 15 bool Cmp(Contest a, Contest b){return a.r < b.r;}
 16 class segTree{
 17     ll a[maxn],s[maxn*4],tag[maxn*4];
 18 public:
 19     segTree(){memset(a,0,sizeof(a));}
 20     void build(int node,int begin,int end){
 21         tag[node]=0;
 22         if(begin==end){
 23             s[node]=a[begin];
 24         }else{
 25             build(2*node,begin,(begin+end)/2);
 26             build(2*node+1,(begin+end)/2+1,end);
 27             s[node]=max(s[2*node],s[2*node+1]);
 28         }
 29     }
 30     void pushdown(int node,int begin,int end){
 31         if(begin==end){
 32             s[node]+=tag[node];
 33             a[begin]=s[node];
 34         }else{
 35             tag[2*node]+=tag[node];
 36             tag[2*node+1]+=tag[node];
 37             s[node]+=tag[node];
 38         }
 39         tag[node]=0;
 40     }
 41     void Change(int node,int begin,int end,int left,int right,ll add){
 42         if(begin>=left&&end<=right){
 43             tag[node]+=add;
 44         }else{
 45             pushdown(node,begin,end);
 46             int m=(begin+end)/2;
 47             if(!(right<begin||left>m)){
 48                 Change(2*node,begin,m,left,right,add);
 49             }
 50             if(!(right<m+1||left>end)){
 51                 Change(2*node+1,m+1,end,left,right,add);
 52             }
 53             s[node] = max(s[2 * node] + tag[2 * node], s[2 * node + 1] + tag[2 * node + 1]);
 54         }
 55     }
 56     ll query(int node,int begin,int end,int left,int right){
 57         pushdown(node,begin,end);
 58         if(begin > right || end < left)return -inf;
 59         if(begin==end)return a[begin];
 60         if(begin >= left && end <= right)return s[node];
 61         ll q1 = query(2 * node, begin, (begin + end) / 2, left, right);
 62         ll q2 = query(2 * node + 1, (begin + end) / 2 + 1, end, left, right);
 63         return max(q1, q2);
 64     }
 65     void Print(){
 66         for(int i = 0; i <= n; i++){
 67             cout<<query(1, 0, n, i, i)<<‘ ‘;
 68         }
 69         cout<<endl;
 70     }
 71 }st;
 72
 73 ll c[maxn], f[maxn];
 74 int main(){
 75 //    freopen("e:\\in.txt","r",stdin);
 76     scanf("%d%d",&n,&m);
 77     for(int i = 1; i <= n; i++)scanf("%lld",&c[i]);
 78     for(int i = 0; i < m; i++)scanf("%d%d%lld",&p[i].l,&p[i].r,&p[i].earn);
 79     sort(p, p + m, Cmp);
 80     int idx = 0;
 81     st.build(1, 0, n);
 82     f[0] = 0;
 83     for(int i = 1; i <= n; i++){
 84         while(idx < m && p[idx].r <= i){
 85             st.Change(1, 0, n, 0, p[idx].l - 1, p[idx].earn);
 86 //            st.Print();
 87             idx++;
 88         }
 89         st.Change(1, 0, n, 0, i - 1, -c[i]);
 90 //        st.Print();
 91         f[i] = st.query(1, 0, n, 0, i - 1);
 92         f[i] = max(f[i - 1], f[i]);
 93         st.Change(1, 0, n, i, i, f[i]);
 94 //        st.Print();
 95 //        cout<<"*********"<<endl;
 96     }
 97 //    for(int i = 0; i <= n; i++)cout<<f[i]<<endl;
 98     cout<<f[n]<<endl;
 99     return 0;
100 }
时间: 2024-10-12 14:08:58

51nod 1611 金牌赛事(动态规划 + 线段树)的相关文章

51nod 1364 最大字典序排列(线段树)

1364 最大字典序排列基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 给出一个1至N的排列,允许你做不超过K次操作,每次操作可以将相邻的两个数交换,问能够得到的字典序最大的排列是什么? 例如:N = 5, {1 2 3 4 5},k = 6,在6次交换后,能够得到的字典序最大的排列为{5 3 1 2 4}. Input 第1行:2个数N, K中间用空格分隔(1 <= N <= 100000, 0 <= K <= 10^9). 第2至N + 1行

51nod 1463 找朋友(线段树+离线处理)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1463 题意: 思路: 好题! 先对所有查询进行离线处理,按照右区间排序,因为k一共最多只有10个,所有在该区间内的B数组,每次枚举K值,通过这样的方式来得到另外一个B值.但是这样得到的B值它在B数组中的位置必须在当前数的左边.如下图:(j为当前数在B数组中的位置,pos为计算得到的另一个B值在数组中的位置) 这两个数的和记录在pos中,这里pos的位置必须在j的左边,假

Codeforces 834D The Bakery - 动态规划 - 线段树

Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought required ingredients and a wonder-oven which can bake several types of cakes, and opened the bakery. Soon the expenses started to overcome the income, so Slastyona decid

【BZOJ2090/2089】[Poi2010]Monotonicity 2 动态规划+线段树

[BZOJ2090/2089][Poi2010]Monotonicity Description 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k].选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1].求出L的最大值. Input 第一行两个正整数,分别表示N和K (N, K <= 500,000).第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6).第三行给出K

51nod 1463 找朋友 (扫描线+线段树)

1463 找朋友  基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80 难度:5级算法题  收藏  关注 给定: 两个长度为n的数列A .B 一个有m个元素的集合K 询问Q次 每次询问[l,r],输出区间内满足|Bi-Bj|∈K 的最大Ai+Aj 数据约定: n,Q<=100000 m <= 10 0<=A[i]<=1000000000 1<=B[i]<=n 1<=K[i]<=n 保证B[i]互不相等 Input n Q m A1 A2 .

Codeforces Round #620 Div2F Animal Observation(前缀和+动态规划+线段树维护)

题意: 作者喜欢观察动物,因此他购买了两个照相机,以拍摄森林中野生动物的视频,一台摄像机的颜色是红色,一台摄像机的颜色是蓝色. 从第1天到第N天,作者将拍摄N天的视频.森林可以分为M个区域,编号从1到M.他将通过以下方式使用相机: 在每个奇数天,将红色相机带到森林中并录制两天的视频. 在每个偶数天,将蓝色相机带到森林中并录制两天的视频. 如果他在第N天使用其中一台摄像机开始录制,则该摄像机仅录制一天. 每个摄像机可以连续观察森林的K个连续的区域. 作者已经获得了有关每天在每个区域看到多少动物的信

51nod 1272 思维/线段树

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1272 1272 最大距离 题目来源: Codility 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 收藏 关注 给出一个长度为N的整数数组A,对于每一个数组元素,如果他后面存在大于等于该元素的数,则这两个数可以组成一对.每个元素和自己也可以组成一对.例如:{5, 3, 6, 3, 4, 2},可以组成11对,如下(数字为下标):

51nod 1287 线段树

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1287 简单的线段树题目,直接写个二分查找大于等于x的最小位置就好了. 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 0x3f3f3f3f 4 #define LL long long 5 const int MAX=50005; 6 int A[MAX]; 7 struct SegTree

kb-07-RMQ线段树--07(动态规划)

RMQ是一类解决区间最值查询的算法的通称:.一共有四类:在代码中有说明: 下面是ST算法,就是动态规划做法: 来看一下ST算法是怎么实现的(以最大值为例): 首先是预处理,用一个DP解决.设a是要求区间最值的数列,f[i,j]表示从第i个数起连续2^j个数中的最大值.例如数列3 2 4 5 6 8 1 2 9 7 ,f[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数.f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……从这里可以看出f[i,0]其实就等于