POJ 3709 K-Anonymous Sequence - 斜率优化dp

描述

给定一个数列 $a$, 分成若干段,每段至少有$k$个数, 将每段中的数减少至所有数都相同, 求最小的变化量

题解

易得到状态转移方程 $F_i = \min(F_j  + sum_i - sum_j - (i - j ) \times a_(j+1) ) $ $ 0 <= j <= i - k$。

把只含$j$ 放在一边, 其他的放在另一边得到:$F_j + j \times a_(j+1)  - sum_j =  i \times a_(j+1)  F_i - sum_i$

然后就可以愉快地套上斜率优化的板子了QuQ

还有这道题我用叉积挂了唔, 数列$a_i$是不严格递增的,点重合就挂了(也有可能是我自己打挂了

代码

 1 #include<cstring>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rd read()
 5 #define rep(i,a,b) for( int i = (a); i <= (b); ++i)
 6 #define per(i,a,b) for( int i = (a); i >= (b); --i)
 7 using namespace std;
 8 typedef long long ll;
 9 
10 const int N = 1e6;
11 const ll inf = 1LL << 61;
12 
13 ll sum[N], f[N], q[N], d[N];
14 int n, T, k;
15 
16 int read() {
17     int X = 0, p = 1; char c = getchar();
18     for(; c > '9' || c < '0'; c = getchar()) if( c == '-') p = -1;
19     for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0';
20     return X * p;
21 }
22 
23 double calc(int a, int b) {
24     double y = f[b] + b * d[b + 1] - sum[b] - f[a] - a * d[a + 1] + sum[a];
25     double x = d[b + 1] - d[a + 1];
26     if(!x && !y) return inf;
27     return y / x;
28 }
29 
30 int main()
31 {
32     T = rd;
33     for(; T; T--) {
34         n = rd; k = rd;
35         rep(i, 1, n) d[i] = rd, sum[i] = sum[i - 1] + d[i];
36         rep(i, k, n) f[i] = sum[i] - i * d[1];
37         rep(i, 0, k - 1) f[i] = inf;
38         int l = 1, r = 1;
39         q[1] = k;
40         rep(i, 2 * k, n) {
41             while(l < r && calc(q[l], q[l + 1]) <= i) l++;
42             f[i] = min(f[i], f[q[l]] + sum[i] - sum[q[l]] - (i - q[l]) * d[q[l] + 1]);
43             while(l < r && calc(q[r - 1], q[r]) >= calc(q[r], i - k + 1)) r--;
44             q[++r] = i - k + 1;
45         }
46         printf("%lld\n", f[n]);
47     }
48 }
View Code

猜你喜欢

转载自www.cnblogs.com/cychester/p/9502226.html