CF321E Ciel and Gondolas

题意:给定序列,将其分成k段。如果[l, r]在一段,那么每对不相同的i,j∈[l, r]都会有ai,j的代价。求最小总代价。

解:提供两种方案。第三种去bzoj贞鱼的n²算法。

决策单调性优化:

对于两个转移点j1 < j2,若在某个点i上j2更优,则i后面的j2全部更优。这就是决策单调性。

有两种写法。一种是维护决策栈(???),我自己YY了一个线段树写法WA飞了。

还有一种是分治。对于一段待转移的区间[l, r],它们的最优转移来自于[L, R]

则对于mid = (l + r) >>1,我们扫一遍[L, R],得到mid的最优转移p

然后分治下去。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int N = 4010, K = 810, INF = 0x3f3f3f3f;
 5 
 6 inline void read(int &x) {
 7     x = 0;
 8     char c = getchar();
 9     while(c < '0' || c > '9') {
10         c = getchar();
11     }
12     while(c >= '0' && c <= '9') {
13         x = (x << 3) + (x << 1) + c - 48;
14         c = getchar();
15     }
16     return;
17 }
18 
19 int f[N][K], n, k, a[N][N];
20 int tag[N], turn;
21 
22 inline void exmin(int &a, int b) {
23     a > b ? a = b : 0;
24     return;
25 }
26 
27 void solve(int L, int R, int l, int r) {
28     if(R < L || r < l) return;
29     //printf("solve %d %d -> %d %d 
", L, R, l, r);
30     if(L == R) {
31         for(int i = l; i <= r; i++) {
32             f[i][turn] = f[R][turn - 1] + a[i][R + 1];
33             //printf("f %d %d = %d + %d 
", i, turn, f[R][turn - 1], a[i][R + 1]);
34         }
35         return;
36     }
37     int mid = (l + r) >> 1, p = L;
38     for(int i = L; i < mid && i <= R; i++) {
39         if(f[mid][turn] > f[i][turn - 1] + a[mid][i + 1]) {
40             p = i;
41             f[mid][turn] = f[i][turn - 1] + a[mid][i + 1];
42             //printf("f %d %d = %d from %d 
", mid, turn, f[mid][turn], p);
43         }
44     }
45     solve(L, p, l, mid - 1);
46     solve(p, R, mid + 1, r);
47     return;
48 }
49 
50 int main() {
51 
52     //freopen("in.in", "r", stdin);
53     //freopen("my.out", "w", stdout);
54 
55     read(n); read(k);
56     for(register int i = 1; i <= n; i++) {
57         for(register int j = 1; j <= n; j++) {
58             read(a[i][j]);
59             if(i < j) a[i][j] = 0;
60         }
61     }
62     for(register int i = 1; i <= n; i++) {
63         for(register int j = n; j >= 1; j--) {
64             a[i][j] += a[i][j + 1];
65         }
66     }
67     for(register int i = 1; i <= n; i++) {
68         for(register int j = n; j >= 1; j--) {
69             a[i][j] += a[i - 1][j];
70         }
71     }
72     memset(f, 0x3f, sizeof(f));
73     f[0][0] = 0;
74     for(int j = 1; j <= k; j++) {
75         turn = j;
76         //printf("turn = %d 
", j);
77         solve(0, n - 1, 1, n);
78         /*for(int i = 1; i <= n; i++) {
79             printf("%d ", f[i][turn]);
80         }
81         printf("
");*/
82     }
83 
84     printf("%d
", f[n][k]);
85     return 0;
86 }
决策单调性优化AC代码

带权二分:我们假装那个函数是凸的。

然后按照套路带权二分一波。

记得一定要让k的变化和D的变化同步,这一点可以通过调整±D来实现。

之后尽量让k小,l端mid,r端要mid - 1。

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 typedef long long LL;
 5 const int N = 4010;
 6 LL INF = 1e17;
 7 
 8 inline void read(LL &x) {
 9     x = 0;
10     char c = getchar();
11     while(c < '0' || c > '9') {
12         c = getchar();
13     }
14     while(c >= '0' && c <= '9') {
15         x = (x << 3) + (x << 1) + c - 48;
16         c = getchar();
17     }
18     return;
19 }
20 
21 int n, k, g[N];
22 LL f[N], a[N][N], D, ans;
23 
24 inline int check(LL mid) {
25     D = mid;
26     for(register int i = 1; i <= n; i++) {
27         f[i] = INF;
28         for(register int j = 0; j < i; j++) {
29             if(f[i] > f[j] + a[i][j + 1] - D) {
30                 f[i] = f[j] + a[i][j + 1] - D;
31                 g[i] = g[j] + 1;
32             }
33             else if(f[i] == f[j] + a[i][j + 1] - D) {
34                 g[i] = std::min(g[i], g[j] + 1);
35             }
36         }
37     }
38     ans = f[n];
39     return g[n];
40 }
41 
42 int main() {
43     scanf("%d%d", &n, &k);
44     for(register int i = 1; i <= n; i++) {
45         for(register int j = 1; j <= n; j++) {
46             read(a[i][j]);
47             if(i < j) a[i][j] = 0;
48         }
49     }
50     for(register int i = 1; i <= n; i++) {
51         for(register int j = n; j >= 1; j--) {
52             a[i][j] += a[i][j + 1];
53         }
54     }
55     for(register int i = 1; i <= n; i++) {
56         for(register int j = n; j >= 1; j--) {
57             a[i][j] += a[i - 1][j];
58         }
59     }
60     //
61 
62     LL l = -a[n][1], r = a[n][1];
63     while(l < r) {
64         LL mid = (l + r + 1) >> 1;
65         int t = check(mid);
66         if(t == k) {
67             printf("%lld
", ans + k * mid);
68             return 0;
69         }
70         if(t < k) l = mid;
71         else r = mid - 1;
72     }
73     check(r);
74     printf("%lld
", ans + k * r);
75     return 0;
76 }
带权二分AC代码
原文地址:https://www.cnblogs.com/huyufeifei/p/10409122.html