[CSP 2019]划分[dp][贪心][单调队列优化]

原题:https://loj.ac/problem/3212

CSP 结束了快一年了我终于来补这道题了、、


首先考虑最暴力的做法:设 $f[i,j]$ 表示左端点为 j,右端点为 i 的这一段卷起来最小的答案,容易得到转移方程

$$f[i,j]=\min_k\{f[j-1,k]+(sum[i]-sum[j-1])^2\}(sum[i]-sum[j-1]\geq sum[j-1]-sum[k-1])$$

复杂度 $O(n^3)$,可以获得 36pts 的好成绩


考虑优化,因为要最小化平方的和,可以贪心的想到我们需要保证每一段的和尽可能的小,这样才能使贡献尽可能小

也就是说,假设需要把 $[i,j]$ 这段卷起来,我们要尽量保证 $i$ 和 $j$ 靠近(使每一段和尽可能小)

因此转移的时候直接取最后一个点就可了,复杂度 $O(n^2)$,可以获得 64pts 的好成绩

吐槽:我考场想出来了这个做法结果读入的时候没开 long long 直接 64->52

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <cstdlib>
 6 #include <cctype>
 7 #include <vector>
 8 #include <queue>
 9 #define inf 100010
10 #define Inf ((int)5e6+10)
11 #define ll long long
12 
13 namespace chiaro {
14 
15 template <class T>
16 inline void read(T& num) {
17     num = 0; register char c = getchar(), up = c;
18     while(!isdigit(c)) up = c, c = getchar();
19     while(isdigit(c)) num = (num * 10) + c - '0', c = getchar();
20     up == '-' ? num = -num : 0;
21 }
22 template <class T>
23 inline void read(T& a, T& b) {read(a); read(b);}
24 
25 ll n, type;
26 ll a[inf];
27 ll sum[inf];
28 ll f[inf];
29 ll l[inf];
30 
31 inline void setting() {
32 #ifdef ONLINE_JUDGE
33     freopen("partition.in", "r", stdin);
34     freopen("partition.out", "w", stdout);
35 #endif 
36 }
37 
38 inline int main () {
39     setting();
40     read(n, type);
41     for(int i = 1; i <= n; i++) read(a[i]);
42     for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
43     l[1] = a[1]; f[1] = a[1] * a[1];
44     for(int i = 2; i <= n; i++) {
45         for(int j = 0; j < i; j++) {
46             if(sum[i] - sum[j] < l[j]) continue;
47             l[i] = sum[i] - sum[j];
48             f[i] = f[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]);
49         }
50     }
51     printf("%lld\n", f[n]);
52     return 0;
53 }
54 
55 }
56 
57 signed main () {return chiaro::main();} 

再来优化

我们可以设 $g[i]$ 表示右端点为 $i$ 的这一卷旁边的那一卷的右端点,我们只需要线性求出 g 本题就解决了

那么有公式

$$设 p=g_i,有 sum_i-sum_p\geq sum_p-sum_{g_p}\rightarrow sum_i\geq 2\times sum_p-sum_{g_p}$$

我们可以设 $calc(x)=2\times sum_x-sum_{g_x}$,那么一个位置 $i$ 合法当且仅当:$sum_i\geq calc(g_i)$

求 g 的时候根据 64pts 的贪心可以得到我们要取能满足合法条件的最靠右侧的点,使用队列

然后 $g_i$ 就是队首,我们再把 $i$ 加入队列,因为如果一个位置 $j$ 他比 $i$ 靠左,贡献还比 $i$ 大,那么显然就不可能是答案,直接弹出,所以这是个单调队列

这样就求玩了 g,然后根据 g 数据线性跳回去统计答案

复杂度 $O(n)$,能获得 88pts 的好成绩

再使用常数小的高精或者 __int128 可以获得 100pts 的成绩

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <cstdlib>
  6 #include <cctype>
  7 #include <vector>
  8 #define inf ((int)4e7 + 10)
  9 #define N (int)(1e5 + 10)
 10 #define ll long long
 11 #ifndef ONLINE_JUDGE
 12 #define __int128 long long
 13 #endif
 14 
 15 namespace chiaro {
 16 
 17 template <class T>
 18 inline void read(T& num) {
 19     num = 0; register char c = getchar(), up = c;
 20     while(!isdigit(c)) up = c, c = getchar();
 21     while(isdigit(c)) num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
 22     up == '-' ? num = -num : 0;
 23 }
 24 template <class T>
 25 inline void read(T& a, T& b) {read(a); read(b);}
 26 template <class T>
 27 inline void read(T& a, T& b, T& c) {read(a); read(b); read(c);}
 28 template <class T>
 29 void print(T num) {(num > 9) ? (print(num / 10), 0) : 0; putchar(num % 10 + '0'); return;}
 30 
 31 const int MOD = (1 << 30) - 1;
 32 
 33 ll n, type, a;
 34 ll sum[inf];
 35 int g[inf];
 36 __int128 ans;
 37 
 38 inline void setting() {
 39 #ifdef ONLINE_JUDGE
 40     freopen("partition.in", "r", stdin);
 41     freopen("partition.out", "w", stdout);
 42 #endif 
 43 }
 44 
 45 inline void input() {
 46     if(type){
 47         int x, y, z, m, now = 1; ll b1, b2, b3, w;
 48         read(x, y, z); read(b1, b2); read(m);
 49         for (register int i = 1; i <= m; ++i) {
 50             int p, l, r; read(p, l, r);
 51             while (now <= p) {
 52                 if (now == 1) w = (b1 % (r - l + 1)) + l, sum[1] = w;
 53                 else if (now == 2) w = (b2 % (r - l + 1)) + l, sum[2] = sum[1] + w;
 54                 else {
 55                     b3 = (x * b2 + y * b1 + z) & MOD;
 56                     w = (b3 % (r - l + 1)) + l, sum[now] = sum[now - 1] + w;
 57                     b1 = b2, b2 = b3;
 58                 }
 59                 ++now;
 60             }
 61         }
 62     } else for(register int i = 1; i <= n; sum[i] = sum[i - 1] + a, ++i) read(a);
 63 }
 64 
 65 class queue {
 66     private:
 67     int a[inf];
 68     int l, r;
 69 
 70     public:
 71     queue () {l = 1, r = 0;}
 72     inline void push(const int& v) {a[++r] = v;}
 73     inline bool empty() {return l > r;}
 74     inline void pop_back() {--r;}
 75     inline void pop_front() {++l;}
 76     inline int back() {return a[r];}
 77     inline int front() {return a[l];}
 78     inline bool have_next() {return r > l;}
 79     inline int next() {return a[l + 1];}
 80 };
 81 
 82 queue Q;
 83 
 84 #define calc(x) ((sum[x] << 1) - sum[g[x]])
 85 
 86 inline int main () {
 87     setting();
 88     read(n, type); input();
 89     Q.push(0);
 90     for(int i = 1; i <= n; ++i) {
 91         while(Q.have_next() && calc(Q.next()) <= sum[i]) Q.pop_front();
 92         g[i] = Q.front();
 93         while(!Q.empty() && calc(Q.back()) >= calc(i)) Q.pop_back();
 94         Q.push(i);
 95     }
 96     register int now = n;
 97     while(now) {
 98         ans += (__int128)(sum[now] - sum[g[now]]) * (sum[now] - sum[g[now]]);
 99         now = g[now];
100     }
101     print(ans);
102     return 0;
103 }
104 
105 }
106 
107 signed main () {return chiaro::main();} 

 

posted @ 2020-08-26 14:58  Chiaro  阅读(214)  评论(0)    收藏  举报