dp??不能确定转移状态。考虑用优先队列储存最优决策点,可是发现当前选择最优不能保证最后最优,在后面可以将之前用过的替换过来。
比如数据:
3 5
4 6
只储存a[i]来决策不能延展到后面的状态,因此每次选择过后把b[i]加入队列,下次选择最优时如果选择到了b[i],则表示用之前选择过的来替换到当前状态。
这里我开了两个优先队列。
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #define ll long long 5 #define RG register 6 using namespace std; 7 8 int n, a[100005], b[100005]; 9 10 priority_queue < int, vector < int > , greater < int > > q1, q2; 11 12 int main ( ) { 13 freopen ( "buy.in", "r", stdin ); 14 freopen ( "buy.out", "w", stdout ); 15 scanf ( "%d", &n ); 16 for ( RG int i = 1; i <= n; i ++ ) 17 scanf ( "%d", &a[i] ); 18 for ( RG int i = 1; i <= n; i ++ ) 19 scanf ( "%d", &b[i] ); 20 ll ans = 0; 21 for ( RG int i = 1; i <= n; i ++ ) { 22 q1.push ( a[i] ); 23 int r1 = 0, r2 = 0; 24 if ( !q1.empty ( ) ) { 25 int x = q1.top ( ); 26 if ( b[i] > x ) r1 = b[i] - x; 27 } 28 if ( !q2.empty ( ) ) { 29 int x = q2.top ( ); 30 if ( b[i] > x ) r2 = b[i] - x; 31 }
32 if ( r1 >= r2 && r1 ) ans += r1, q1.pop ( ), q2.push ( b[i] ); 33 else if ( r2 > r1 && r2 ) ans += r2, q2.pop ( ), q2.push ( b[i] ); 34 } 35 printf ( "%I64d", ans ); 36 return 0; 37 }
记录前缀和,可以发现,从某一个点为起点时,向后延展出去的长度中一定有i到i+s这一段,所以用前缀和最大值建一棵线段树,每次查找i+s-1到i+e-1段的最大值,减去i-1的前缀和比较答案即可。
1 #include<iostream> 2 #include<cstdio> 3 #define ll long long 4 using namespace std; 5 6 int n, s, e, a[100005]; 7 ll pre[100005], TR[400005]; 8 9 void update ( int nd ) { 10 TR[nd] = max ( TR[nd << 1], TR[nd << 1 | 1] ); 11 } 12 13 void build ( int nd, int l, int r ) { 14 if ( l == r ) { 15 TR[nd] = pre[l]; 16 return ; 17 } 18 int mid = ( l + r ) >> 1; 19 build ( nd << 1, l, mid ); 20 build ( nd << 1 | 1, mid + 1, r ); 21 update ( nd ); 22 } 23 24 ll query ( int nd, int l, int r, int L, int R ) { 25 if ( l >= L && r <= R ) return TR[nd]; 26 int mid = ( l + r ) >> 1; 27 ll ans = -1e9; 28 if ( L <= mid ) ans = max ( ans, query ( nd << 1, l, mid, L, R ) ); 29 if ( R > mid ) ans = max ( ans, query ( nd << 1 | 1, mid + 1, r, L, R ) ); 30 return ans; 31 } 32 33 int main ( ) { 34 freopen ( "invest.in", "r", stdin ); 35 freopen ( "invest.out", "w", stdout ); 36 scanf ( "%d%d%d", &n, &s, &e ); 37 for ( int i = 1; i <= n; i ++ ) { 38 scanf ( "%d", &a[i] ); 39 pre[i] = pre[i-1] + a[i]; 40 } 41 build ( 1, 1, n ); 42 ll ans = 0; 43 for ( int i = 1; i <= n; i ++ ) { 44 if ( i + s - 1 > n ) break; 45 ll x = query ( 1, 1, n, i + s - 1, i + e - 1 ); 46 ans = max ( x - pre[i-1], ans ); 47 } 48 printf ( "%I64d", ans ); 49 return 0; 50 }
关键时候manacher忘了怎么写!!先manacher一遍处理出以每个点为中心点的最长回文串长度,一定是奇数。开桶记录每个长度出现次数,从大到小枚举长度l,每次把l-2的次数加上l的次数,因为l的长度满足回文串l-2一定满足(同一中心点,注意k要开long long!
1 #include<iostream> 2 #include<cstdio> 3 #define ll long long 4 #define mod 19930726 5 using namespace std; 6 7 ll max_r[2000005]; 8 int n; 9 ll k; 10 ll ans = 1, flag[1000005]; 11 12 char M[2000005], a[1000005]; 13 14 inline ll min ( ll a, int b ) { 15 return a < b ? a : b; 16 } 17 18 ll mi ( ll a, ll b ) { 19 ll an = 1; 20 for ( ; b; b >>= 1, a = a * a % mod ) 21 if ( b & 1 ) an = an * a % mod; 22 return an; 23 } 24 25 void manacher ( ) { 26 M[0] = '@'; 27 for ( int i = 1; i <= n; i ++ ) { 28 M[2 * i - 1] = '#'; 29 M[2 * i] = a[i]; 30 } 31 M[2 * n + 1] = '#'; M[2 * n + 2] = '$'; 32 int center = 0; ll mx = 0; 33 int side = n * 2 + 1; 34 for ( int i = 1; i <= n * 2 + 1; i ++ ) { 35 if ( mx > i ) max_r[i] = min ( mx - (ll)i, max_r[center * 2 - i] ); 36 else max_r[i] = 1; 37 while ( M[max_r[i]+i] == M[i-max_r[i]] ) max_r[i] ++; 38 if ( mx < i + max_r[i] ) { 39 mx = i + max_r[i]; center = i; 40 } 41 } 42 } 43 44 int main ( ) { 45 freopen ( "rehearse.in", "r", stdin ); 46 freopen ( "rehearse.out", "w", stdout ); 47 scanf ( "%d%I64d ", &n, &k ); 48 scanf ( "%s", a + 1 ); 49 manacher ( ); 50 ll MA = 0; 51 for ( int i = 1; i <= n; i ++ ) { 52 max_r[i*2] --; 53 flag[max_r[i*2]] ++; 54 MA = max ( MA, max_r[i*2] ); 55 } 56 ll pos = MA; 57 while ( k > 0 ) { 58 ans = ( ans * mi ( pos, min ( flag[pos], k ) ) ) % mod; 59 flag[pos-2] += flag[pos]; 60 k -= flag[pos]; 61 pos = pos - 2; 62 } 63 printf ( "%I64d", ans ); 64 return 0; 65 }