1. 程式人生 > >[BZOJ1122][POI2008]賬本BBB 單調隊列+後綴和

[BZOJ1122][POI2008]賬本BBB 單調隊列+後綴和

perf hang asa namespace long long sample n) pac 操作

Description

一個長度為n的記賬單,+表示存¥1,-表示取¥1。現在發現記賬單有問題。一開始本來已經存了¥p,並且知道最後賬戶上還有¥q。你要把記賬單修改正確,使得 1:賬戶永遠不會出現負數; 2:最後賬戶上還有¥q。你有2種操作: 1:對某一位取反,耗時x; 2:把最後一位移到第一位,耗時y。

Input

The first line contains 5 integers n, p, q, x and y (1  n  1000000, 0  p;q  1000000, 1  x;y  1000), separated by single spaces and denoting respectively: the number of transactions done by Byteasar, initial and final account balance and the number of seconds needed to perform a single turn (change of sign) and move of transaction to the beginning. The second line contains a sequence of n signs (each a plus or a minus), with no spaces in-between. 1 ≤ n ≤ 1000000, 0 ≤ p ,q ≤ 1000000, 1 ≤x,y ≤ 1000)

Output

修改消耗的時間

Sample Input

9 2 3 2 1
---++++++

Sample Output

3

Solution

做法:單調隊列+後綴和

好難啊這題...

兩種操作,我們可以枚舉第二種操作的次數,然後算出第一種操作的情況

枚舉操作2的話其實就是斷鏈成環,然後枚舉起點

對於操作1的求解,我們可以搞個單調隊列來弄一下

首先維護一個後綴和,然後用單調隊列處理出對於每個起點賬本最高能達到多少

然後就分類討論一下就可以了

如果序列和再加上$p$大於$q$的話就對$+$取反

否則就對後面的$-$取反

#include <bits/stdc++.h>

using
namespace std ; #define ll long long const int N = 2e6 + 10 ; ll n , p , q , x , y ; char s[ N ] ; ll a[ N ] , b[ N ]; ll sum[ N ] ; deque <int> Q ; int main() { scanf( "%lld%lld%lld%lld%lld" , &n , &p , &q , &x , &y ) ; scanf( "%s" , s + 1 ) ;
for( int i = 1 ; i <= n ; i ++ ) { a[ i ] = s[ i ] == + ? 1 : -1 ; a[ i + n ] = a[ i ] ; } for( int i = n * 2 ; i ; i -- ) { sum[ i ] = sum[ i + 1 ] + a[ i ] ; } for( int i = n * 2 ; i ; i -- ) { if( i <= n ) b[ i ] = sum[ i ] - sum[ Q.back() ] ; while( !Q.empty() && sum[ Q.front() ] <= sum[ i ] ) Q.pop_front() ; Q.push_front( i ) ; while( Q.back() >= i + n ) Q.pop_back() ; } ll ans = 0x7fffffff ; for( int i = 1 ; i <= n ; i ++ ) { ll now = y * ( ( n - i + 1 ) % n ) ; if( p + sum[ n + 1 ] >= q ) now += ( p + sum[ n + 1 ] - q ) / 2 * x ; else { now += ( q- p - sum[ n + 1 ] ) / 2 * x ; b[ i ] += ( q - p - sum[ n + 1 ] ) ; } if( p + b[ i ] < 0 ) now -= ( p + b[ i ] - 1 ) / 2 * 2 * x ; ans = min( ans , now ) ; } printf( "%lld\n" , ans ) ; }

[BZOJ1122][POI2008]賬本BBB 單調隊列+後綴和