CF1107

我哭了......什麼鬼題我怎麼都不會...果真教育場是教我作人的...node

打的虛擬賽,286名...太菜了。EF都是可作題我都沒寫出來...G題大水題我竟然沒看...數組

 


B:設g(i) = i的各位數字之和,f(i) = g(i) < 10 ? g(i) : f(g(i))
ide

多組詢問,每次求g(i) = x的第k大的i。k <= 1e12優化

解:這題一開始把我看得一愣一愣的,啥玩意?數位DP?怎麼放在第二題?spa

而後冷靜下來打了一波表,發現是個SB找規律......從1開始每一個數的g()值必定是12345....912...912...9.....code

這就很OK了,輸出9(k-1)+x便可。blog

 


C:給定字符串,每一個位置都有一個權值。排序

你要選出一個子序列,使得這些位置上的權值和最大,且沒有哪一個字符連續出現超過k次。字符串

權值非負。get

解:稍加思索......直接堆啊!每連續的一段貪心選最大的k個便可。

(若是權值能夠爲負怎麼辦?首先確定能夠暴力DP,f[i][j][k]表示前i個選j個,第i個必須選,末尾有連續k個s[i]的最大權值。DP優化...不會...複雜度更優的作法......不會...)

 


D:給定n x n的01矩陣,你要嘗試把它壓縮,每a x a個字符壓縮成一個字符,要求這a2個字符所有同樣。

顯然a必須是n的約數。求最大的a。

解:稍加思索...好像直接n2gcd就行啊?這麼簡單?

a必須是每行/列全部連續段的長度的約數。

感受沒錯就寫了。爲了加速把gcd記憶化了,還加了個剪枝。而後就A了...

 


E:給定長爲n的01序列與數組a。

你每次能夠選擇其中連續的一段0或1消掉,若是長度爲x則可以獲得a[x]的收益。求最大收益。

a[x]非負,n <= 100。

解:我是SB系列......

看到題就想到了區間DP,好比啥神題ZUMA......嘗試一下。

f[i][j][k]表示[i, j]這一段後面接長爲k的0/1時徹底消除的最大收益,發現徹底不行...棄療了。

看題解,發現是f[i][j][k][0/1]表示把[i,j]這一段消成k個0/1的最大收益,這樣就可行了......

答案是f[1][n][0][0]或f[1][n][0][1]。

轉移就是枚舉這k個0/1其中第一個在哪裏。f[l][r][0][0/1]的轉移是f[l][r][k][0/1] + a[k]

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 typedef long long LL;
 5 const int N = 110;
 6 const LL INF = 0x3f3f3f3f3f3f3f3f;
 7 
 8 LL f[N][N][N][2], a[N];
 9 char s[N];
10 
11 inline void exmax(LL &a, const LL &b) {
12     a < b ? a = b : false;
13     return;
14 }
15 
16 int main()  {
17     memset(f, ~0x3f, sizeof(f));
18     int n;
19     scanf("%d%s", &n, s + 1);
20     for(int i = 1; i <= n; i++) {
21         scanf("%lld", &a[i]);
22         s[i] -= '0';
23         f[i][i][0][0] = f[i][i][0][1] = a[1];
24         f[i][i - 1][0][0] = f[i][i - 1][0][1] = f[i][i][1][s[i]] = 0;
25     }
26     f[n + 1][n][0][0] = f[n + 1][n][0][1] = 0;
27     for(int len = 2; len <= n; len++) {
28         for(int l = 1; l + len - 1 <= n; l++) {
29             int r = l + len - 1;
30             for(int k = len; k >= 1; k--) {
31                 // f[l][r][k][0] f[l][r][k][1]
32                 for(int p = l; p + k - 1 <= r; p++) {
33                     exmax(f[l][r][k][s[p]], f[l][p - 1][0][0] + f[p + 1][r][k - 1][s[p]]);
34                 }
35                 exmax(f[l][r][0][0], f[l][r][k][0] + a[k]);
36                 exmax(f[l][r][0][0], f[l][r][k][1] + a[k]);
37             }
38         }
39     }
40 
41     printf("%lld", f[1][n][0][0]);
42     return 0;
43 }
AC代碼

 


F:有n個貸款,你能夠在每月初選擇第i個貸款,獲得ai,以後的ki個月就要每月底還款bi(包括本月)。

你會在某個月中間攜鉅款潛逃,求你最多能帶走多少錢。

每月只能貸一筆款,每筆貸款也只能被貸一次。n <= 500

解:回想起修車的套路,咱們能夠計算在潛逃前i個月貸j的收益是a[j] - b[j] * min(i - 1, k[j])

而後想到一個相似揹包的DP,能夠設f[i][j]表示潛逃前j個月只貸前i筆款時的最大收益。

而後發現我涼了。。。緣由是貸款的順序跟揹包不同,揹包無序,這個有序。

而後發現能夠費用流,興沖沖打了一波,TLE...

結束後發現是KM,趕快去現場學一波...DFSTLE...BFSKM就是大毒瘤至今不會...

我瘋了。看別人的提交,TM是DP......

仍是以前那個狀態,可是多了個排序,按bi排序!我是大SB...

個人理解是這樣的,對於每一個貸款你顯然有三種選擇,要麼老實還錢,要麼畏罪潛逃,要麼不去選。

如今考慮兩個畏罪潛逃的貸款的前後順序。

兩個的ai都選了,因此就只跟bi有關了,因此bi大的應該越晚越好。

這樣就能夠DP了......

具體的轉移:f[i][j] = max(f[i - 1][j], f[i][j - 1], //不選i/第j天不選

f[i - 1][j - 1] + a[i] - b[i] * (j - 1), //第j天選i,畏罪潛逃

f[i - 1][j] + a[i] - b[i] * k[i]) //好久之前的某一天選了i

有一個要注意的地方是,你f[i][0]的地方也要轉移,不然f[i][1]的值會出錯......

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 
 5 typedef long long LL;
 6 const int N = 510;
 7 
 8 struct Node {
 9     LL a, b, k;
10     inline bool operator <(const Node &w) const {
11         return b > w.b;
12     }
13 }node[N];
14 
15 LL f[N][N];
16 
17 int main() {
18     int n;
19     scanf("%d", &n);
20     for(int i = 1; i <= n; i++) {
21         scanf("%lld%lld%lld", &node[i].a, &node[i].b, &node[i].k);
22     }
23     std::sort(node + 1, node + n + 1);
24     for(int i = 1; i <= n; i++) { // use i-th credit
25         LL a = node[i].a, b = node[i].b, k = node[i].k;
26         f[i][0] = std::max(f[i - 1][0], f[i - 1][0] + a - b * k);
27         for(int j = 1; j <= n; j++) { // j days before 
28             // f[i][j]
29             f[i][j] = std::max(f[i - 1][j], f[i][j - 1]);
30             // today use i
31             f[i][j] = std::max(f[i][j], f[i - 1][j - 1] + a - b * (j - 1));
32             f[i][j] = std::max(f[i][j], f[i - 1][j] + a - b * k);
33         }
34     }
35     printf("%lld", f[n][n]);
36     return 0;
37 }
AC代碼

 


G:給定數組c和d。你要選出一段子區間,獲得的收益是a * len - ∑ci - max(di+1 - di)2

保證di遞增,求最大收益。

解:發現di遞增好像沒用......

首先考慮枚舉右端點,發現不行,而後考慮枚舉哪一個△d做爲max,這就很OK了,在左右各取前綴和max/min便可。

這道題比前面兩題友善多了,我竟然沒看......太SB了。

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 typedef long long LL;
 5 const int N = 300010;
 6 const LL INF = 0x3f3f3f3f3f3f3f3f;
 7 
 8 int n, pw[N], p[N], top, left[N], right[N];
 9 LL A, d[N], val[N], sum[N], b[N];
10 LL STmax[N][20], STmin[N][20];
11 
12 inline LL getMax(int l, int r) {
13     int t = pw[r - l + 1];
14     return std::max(STmax[l][t], STmax[r - (1 << t) + 1][t]);
15 }
16 
17 inline LL getMin(int l, int r) {
18     int t = pw[r - l + 1];
19     return std::min(STmin[l][t], STmin[r - (1 << t) + 1][t]);
20 }
21 
22 int main() {
23     scanf("%d%lld", &n, &A);
24     LL ans = 0;
25     for(int i = 1; i <= n; i++) {
26         scanf("%lld%lld", &b[i], &val[i]);
27         d[i] = b[i] - b[i - 1];
28         val[i] = A - val[i];
29         sum[i] = sum[i - 1] + val[i];
30         STmax[i][0] = STmin[i][0] = sum[i];
31         ans = std::max(ans, val[i]);
32     }
33     for(int i = 2; i <= n; i++) {
34         pw[i] = pw[i >> 1] + 1;
35     }
36     for(int j = 1; j <= pw[n]; j++) {
37         for(int i = 0; i + (1 << j) - 1 <= n; i++) {
38             STmax[i][j] = std::max(STmax[i][j - 1], STmax[i + (1 << (j - 1))][j - 1]);
39             STmin[i][j] = std::min(STmin[i][j - 1], STmin[i + (1 << (j - 1))][j - 1]);
40         }
41     }
42     
43     d[1] = INF;
44     p[++top] = 1;
45     for(int i = 2; i <= n; i++) {
46          while(top && d[p[top]] <= d[i]) {
47              top--;
48          }
49          left[i] = p[top];
50          p[++top] = i;
51     }
52     top = 1;
53     d[n + 1] = INF;
54     for(int i = 2; i <= n + 1; i++) {
55         while(top && d[p[top]] < d[i]) {
56             right[p[top]] = i;
57             top--;
58         }
59         p[++top] = i;
60     }
61 
62     for(int i = 2; i <= n; i++) {
63         LL t = getMax(i, right[i] - 1) - getMin(left[i] - 1, i - 2) - d[i] * d[i];
64         ans = std::max(ans, t);
65     }
66 
67     printf("%lld", ans);
68     return 0;
69 }
AC代碼

 


太菜了...

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息