马戏团
luoguP7867 马戏团
题意:
先分析一下题意,简化一下就是:
给定一个序列,然后给出几个区间的左右端点和其价值,让你选择其中的几个,总花费是区间的在序列上的并集之和,总价值是这些选择的区间的价值之和,最后要求的答案就是总价值减总花费的最大值。
解法
我们设 f[i] 表示前 i 个舞台且以第 i 个舞台作为最后一个区间的右端点所能产生的最大利益。然后先处理出 sum ( j,i ) 表示所有区间的左右端点在 j ~ i 这段里面的价值之和 和 s ( j,i ) 表示第 j 个到第 i 个舞台所要的费用。
显然,我们最后选完区间,会发现我们选择的所有区间一定是几个不相交的连续区间,那我们考虑枚举每个点当做某个区间的右端点,再枚举当前这个区间的左端点,求一个当前这个区间的最大值,最后再把每一个区间的最大值加起来,就是答案了。
sum ( j,i ) 的处理我们可以用一个vector数组 vc[i] 记录以 i 为右端点的区间,然后我们每次枚举 i 的时候就把新的 i 数组内的区间的影响加进去,每一个区间我们会影响的 sum ( j,i ) 里面的 j 值就只有当 j <= 当前区间的左端点 的时候会有影响,所以我们把 11 到当前这个区间的左端点给区间加上当前区间的价值( 可能会有点绕, 仔细想想就明白了)。
s(j,i) 的处理就用一个前缀和就好了。
由此,我们可以得出这么一个方程式:
f[i] = max ( f[i-1] , max ( f[j-1] + sum ( j,i ) + s ( j,i ) (1<=j<=i) ) )
上面那个式子第二个 max 之前的东西都很好理解,说一下第二个 max 里面的东西。
f[j-1] 表示第 j 个之前最大的利益,所以这后半部分的式子表示有一个从 j 到 i 的区间我们全部选择。
这样子我们就得到了一个时间复杂度是 O(n^2) 级别的解法,但是数据范围是 1e6,所以肯定会炸,考虑优化。
我们发现这整个式子其实是由两个 max 组成的,而且我们发现第一个 max 可以 O(1) 做,第二个 max 可以用线段树来优化,而且我们加入新的区间时也可以用线段树来区间加,所以总复杂度为 O(n log n)。
具体实现的话,看代码吧。码风稍微有点奇怪,希望不影响阅读。
Code
#include <iostream>
#include <vector>
#include <cstdio>
using namespace std ;
const long long INF = 0x3f3f3f3f3f3f3f3f ;
struct Node
{
int l , r ;
long long v ;
} q[1000005] ;
int n , m ;
long long s[1000005] , f[1000005] , t[4000005] , lz[4000005] ;
vector < int > vc[1000005] ;
void build ( int k , int l , int r )
{
if ( l == r )
{
t [ k ] = s [ l - 1 ] ;
return ;
}
int mid = ( l + r ) >> 1 ;
build ( k << 1 , l , mid ) ;
build ( k << 1 | 1 , mid + 1 , r ) ;
t [ k ] = max ( t [ k << 1 ] , t [ k << 1 | 1 ] ) ;
}
void pushdown ( int k )
{
if ( lz [ k ] )
{
t [ k << 1 ] += lz [ k ] ;
t [ k << 1 | 1 ] += lz [ k ] ;
lz [ k << 1 ] += lz [ k ] ;
lz [ k << 1 | 1 ] += lz [ k ] ;
lz [ k ] = 0 ;
}
}
void change ( int k , int l , int r , int x , int y , long long z )
{
if ( x <= l && r <= y )
{
t [ k ] += z ;
lz [ k ] += z ;
return ;
}
pushdown ( k ) ;
int mid = ( l + r ) >> 1 ;
if ( x <= mid )
change ( k << 1 , l , mid , x , y , z ) ;
if ( y > mid )
change ( k << 1 | 1 , mid + 1 , r , x , y , z ) ;
t [ k ] = max ( t [ k << 1 ] , t [ k << 1 | 1 ] ) ;
}
long long query ( int k , int l , int r , int x , int y )
{
if ( x <= l && r <= y )
return t [ k ] ;
pushdown ( k ) ;
int mid = ( l + r ) >> 1 ;
long long res = -INF ;
if ( x <= mid )
res = query ( k << 1 , l , mid , x , y ) ;
if ( y > mid )
res = max ( res , query ( k << 1 | 1 , mid + 1 , r , x , y ) ) ;
return res ;
}
int main ( )
{
cin >> n >> m ;
for ( int i = 1 ; i <= n ; ++ i )
{
long long x ;
cin >> x ;
s [ i ] = s [ i - 1 ] + x ;
}
for ( int i = 1 ; i <= m ; ++ i )
{
cin >> q [ i ] .l >> q [ i ] .r >> q [ i ] .v ;
vc [ q [ i ] .r ] .push_back ( i ) ;
}
f [ 0 ] = 0 ;
build ( 1 , 1 , n ) ;
for ( int i = 1 ; i <= n ; ++ i )
{
for ( int j = 0 ; j < ( int ) vc [ i ] .size ( ) ; ++ j )
change ( 1 , 1 , n , 1 , q [ vc [ i ] [ j ] ] .l , q [ vc [ i ] [ j ] ] .v ) ;
f [ i ] = max ( f [ i - 1 ] , query ( 1 , 1 , n , 1 , i ) - s [ i ] ) ;
change ( 1 , 1 , n , i + 1 , i + 1 , f [ i ] ) ;
}
cout << f [ n ] << endl ;
return 0 ;
}

浙公网安备 33010602011771号