zoukankan      html  css  js  c++  java
  • CF1107

    我哭了......什么鬼题我怎么都不会...果然教育场是教我做人的...

    打的虚拟赛,286名...太菜了。EF都是可做题我都没写出来...G题大水题我居然没看...


    B:设g(i) = i的各位数字之和,f(i) = g(i) < 10 ? g(i) : f(g(i))

    多组询问,每次求g(i) = x的第k大的i。k <= 1e12

    解:这题一开始把我看得一愣一愣的,啥玩意?数位DP?怎么放在第二题?

    然后冷静下来打了一波表,发现是个SB找规律......从1开始每个数的g()值一定是12345....912...912...9.....

    这就很OK了,输出9(k-1)+x即可。


    C:给定字符串,每个位置都有一个权值。

    你要选出一个子序列,使得这些位置上的权值和最大,且没有哪个字符连续出现超过k次。

    权值非负。

    解:稍加思索......直接堆啊!每连续的一段贪心选最大的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代码

    太菜了...

  • 相关阅读:
    强制数据类型转换之Number类型
    强制类型转换之String类型
    数据类型之字符串类型与Number类型
    favicon.ico是什么?
    什么是自幂数?
    JavaScript基础之变量的自增与自减
    CMD 命令
    那些看完很有同感的语句
    HTML5 canvas 元素
    HTML的多媒体属性
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10349228.html
Copyright © 2011-2022 走看看