题目大意
给定一个
n
个元素,每个元素有一个质子数
先要将这些元素分成若干个连续的段。每段里不能存在质子数相同的元素,这个段的代价就是所有段内元素中子数的最大值。
求最小代价和。
Data Constraint
n≤100000
题解
设
fi
表示做完前
i
个位置最小的代价是多少。
显然我们可以枚举上一段的结束位置来转移,这是
fi=min(fj+max{bj+1..i})
所以我们可以用一个单调栈来计算每个元素作为最大值影响的区间,然后在线段树上维护
fi+bi+1
的值。
时间复杂度: O(nlogn)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 100000 + 10
struct Note {
int l , r , v ;
Note ( int L = 0 , int R = 0 , int V = 0 ) { l = L , r = R , v = V ; }
} S[N] ;
struct Tree {
int val , del ;
} T[4*N] ;
int a[N] , b[N] , f[N] ;
int Last[N] , Pre[N] , Smax[N] ;
int n , ret ;
void Build( int v , int l , int r ) {
if ( l == r ) {
T[v].val = f[l-1] + b[l] ;
return ;
}
int mid = (l + r) / 2 ;
Build( v + v , l , mid ) ;
Build( v + v + 1 , mid + 1 , r ) ;
T[v].val = min( T[v+v].val , T[v+v+1].val ) ;
}
void Update( int v ) {
if ( !T[v].del ) return ;
int ls = v + v , rs = v + v + 1 ;
T[ls].val += T[v].del ;
T[rs].val += T[v].del ;
T[ls].del += T[v].del ;
T[rs].del += T[v].del ;
T[v].del = 0 ;
}
void Search( int v , int l , int r , int x , int y ) {
if ( l == x && r == y ) {
ret = min( ret , T[v].val ) ;
return ;
}
Update(v) ;
int mid = (l + r) / 2 ;
if ( y <= mid ) Search( v + v , l , mid , x , y ) ;
else if ( x > mid ) Search( v + v + 1 , mid + 1 , r , x , y ) ;
else {
Search( v + v , l , mid , x , mid ) ;
Search( v + v + 1 , mid + 1 , r , mid + 1 , y ) ;
}
T[v].val = min( T[v+v].val , T[v+v+1].val ) ;
}
void Change( int v , int l , int r , int x , int y , int delta ) {
if ( l == x && r == y ) {
T[v].val += delta ;
T[v].del += delta ;
return ;
}
Update(v) ;
int mid = (l + r) / 2 ;
if ( y <= mid ) Change( v + v , l , mid , x , y , delta ) ;
else if ( x > mid ) Change( v + v + 1 , mid + 1 , r , x , y , delta ) ;
else {
Change( v + v , l , mid , x , mid , delta ) ;
Change( v + v + 1 , mid + 1 , r , mid + 1 , y , delta ) ;
}
T[v].val = min( T[v+v].val , T[v+v+1].val ) ;
}
int main() {
freopen( "array.in" , "r" , stdin ) ;
freopen( "array.out" , "w" , stdout ) ;
scanf( "%d" , &n ) ;
for (int i = 1 ; i <= n ; i ++ ) scanf( "%d%d" , &a[i] , &b[i] ) ;
for (int i = 1 ; i <= n ; i ++ ) {
Pre[i] = Last[a[i]] ;
Last[a[i]] = i ;
Smax[i] = max( Smax[i-1] , Pre[i] ) ;
}
Build( 1 , 1 , n ) ;
int top = 0 ;
for (int i = 1 ; i <= n ; i ++ ) {
Note now = Note( i , i , b[i] ) ;
while ( top && b[i] > S[top].v ) {
Change( 1 , 1 , n , S[top].l , S[top].r , b[i] - S[top].v ) ;
now.l = S[top].l ;
top -- ;
}
S[++top] = now ;
ret = 0x7FFFFFFF ;
Search( 1 , 1 , n , Smax[i] + 1 , i ) ;
if ( i == n ) printf( "%d\n" , ret ) ;
else Change( 1 , 1 , n , i + 1 , i + 1 , ret ) ;
}
return 0 ;
}
以上.