zoukankan      html  css  js  c++  java
  • CF集萃2

    CF1155D - Beautiful Array

    题意:给你一个序列和x,你可以选择任意一个子串(可以为空)乘上x,使得得到的序列最大子串和最大。求这个最大值。30w,2s。

    解:设fi,0/1/2表示序列前i个数还没乘x/正在乘x/乘完了x的最大后缀和。答案就是这个DP数组的最大值。

     1 #include <bits/stdc++.h>
     2 
     3 typedef long long LL;
     4 const int N = 300010;
     5 
     6 LL a[N], x, f[N][3];
     7 int n;
     8 
     9 int main() {
    10     scanf("%d%lld", &n, &x);
    11     for(int i = 1; i <= n; i++) {
    12         scanf("%lld", &a[i]);
    13     }
    14     LL ans = 0;
    15     for(int i = 1; i <= n; i++) {
    16         f[i][0] = std::max(a[i], f[i - 1][0] + a[i]);
    17         f[i][1] = std::max(std::max(a[i] * x, f[i - 1][1] + a[i] * x), f[i - 1][0] + a[i] * x);
    18         f[i][2] = std::max(a[i], std::max(f[i - 1][2] + a[i], f[i - 1][1] + a[i]));
    19         ans = std::max(std::max(ans, f[i][0]), std::max(f[i][1], f[i][2]));
    20     }
    21     printf("%lld
    ", ans);
    22     return 0;
    23 }
    AC代码

    本题还有一种线段树做法。从左往右扫一遍,在每个位置考虑乘x的那一段的右端点在当前位置时的最大值。

    那么右边要加上一个最大前缀。左边要在线段树上每个位置,维护以当前位置为乘x左端时的最大值。就是最大后缀 + (x - 1)a[i]

    然后求个最大值就行了。

    CF1117C - Magic Ship

    题意:你要从(x1,y1)到(x2,y2),你每天会走一格,还会被风吹一格。风向有个长为n的循环节。求最少要几天到达。n <= 1e5,坐标<=1e9。

    解:发现某一天到达之后,接下来所有天都有办法到达。于是二分。

     1 #include <bits/stdc++.h>
     2 
     3 typedef long long LL;
     4 const int N = 100010;
     5 
     6 int x1, x2, Y1, Y2, n;
     7 char str[N];
     8 int dx[N], dy[N];
     9 
    10 template <typename T> inline T abs(T x) {
    11     return x < 0 ? -x : x;
    12 }
    13 
    14 inline bool check(LL k) {
    15     LL x = x1 + (k / n) * dx[n] + dx[k % n];
    16     LL y = Y1 + (k / n) * dy[n] + dy[k % n];
    17     return abs(x - x2) + abs(y - Y2) <= k;
    18 }
    19 
    20 int main() {
    21     scanf("%d%d%d%d", &x1, &Y1, &x2, &Y2);
    22 
    23     scanf("%d", &n);
    24     scanf("%s", str + 1);
    25 
    26     for(int i = 1; i <= n; i++) {
    27         dx[i] = dx[i - 1];
    28         dy[i] = dy[i - 1];
    29         if(str[i] == 'U') {
    30             dy[i]++;
    31         }
    32         if(str[i] == 'D') {
    33             dy[i]--;
    34         }
    35         if(str[i] == 'L') {
    36             dx[i]--;
    37         }
    38         if(str[i] == 'R') {
    39             dx[i]++;
    40         }
    41     }
    42 
    43     LL l = 0, r = 1e18;
    44     while(l < r) {
    45         LL mid = (l + r) >> 1;
    46         if(check(mid)) {
    47             r = mid;
    48         }
    49         else {
    50             l = mid + 1;
    51         }
    52     }
    53 
    54     if(r == 1e18) r = -1;
    55     printf("%lld
    ", r);
    56     return 0;
    57 }
    AC代码

    CF1117D - Magic Gems

    题意:你有很多个魔法宝石,每个可以变成m个普通宝石。你需要选出若干个魔法宝石,然后再选出若干个把它们变成普通宝石,以此来填满n个空格。问方案数。n <= 1e18,m <= 100。

    解:枚举选多少个魔法宝石,发现答案其实就是这个东西:

    打表后发现fn = fn-1 + fn-m。然而我们还有一种更优秀的推法:考虑最后一个空位是普通宝石还是魔法宝石即可。矩阵快速幂即可。

     1 #include <bits/stdc++.h>
     2 
     3 typedef long long LL;
     4 const int N = 110, MO = 1e9 + 7;
     5 
     6 int a[N][N], c[N][N], m, ans[N][N];
     7 LL n;
     8 
     9 inline void out(int (*a)[N]) {
    10 
    11     for(int i = 1; i <= m; i++) {
    12         for(int j = 1; j <= m; j++) {
    13             printf("%d ", a[i][j]);
    14         }
    15         puts("");
    16     }
    17     puts("");
    18 
    19     return;
    20 }
    21 
    22 inline void mul() {
    23     memset(c, 0, sizeof(c));
    24     for(int k = 1; k <= m; k++) {
    25         for(int i = 1; i <= m; i++) {
    26             if(!ans[i][k]) {
    27                 continue;
    28             }
    29             for(int j = 1; j <= m; j++) {
    30                 c[i][j] = (c[i][j] + 1ll * ans[i][k] * a[k][j] % MO) % MO;
    31             }
    32         }
    33     }
    34     memcpy(ans, c, sizeof(ans));
    35     return;
    36 }
    37 
    38 inline void mulself() {
    39     memset(c, 0, sizeof(c));
    40     for(int k = 1; k <= m; k++) {
    41         for(int i = 1; i <= m; i++) {
    42             if(!a[i][k]) continue;
    43             for(int j = 1; j <= m; j++) {
    44                 c[i][j] = (c[i][j] + 1ll * a[i][k] * a[k][j] % MO) % MO;
    45             }
    46         }
    47     }
    48     memcpy(a, c, sizeof(c));
    49     return;
    50 }
    51 
    52 int main() {
    53     scanf("%lld%d", &n, &m);
    54 
    55     if(n < m) {
    56         printf("1");
    57         return 0;
    58     }
    59 
    60     for(int i = 1; i < m; i++) {
    61         a[i + 1][i] = 1;
    62     }
    63     a[1][m] = a[m][m] = 1;
    64     for(int i = 1; i <= m; i++) ans[i][i] = 1;
    65 
    66     LL t = n - m;
    67     //out(a);
    68     //out(ans);
    69     while(t) {
    70         if(t & 1) {
    71             mul();
    72             //out(ans);
    73         }
    74         mulself();
    75         //out(a);
    76         t = t >> 1;
    77     }
    78 
    79     int res = 0;
    80     for(int i = 1; i <= m; i++) {
    81         res = (res + ans[i][m]) % MO;
    82     }
    83     res = (res + ans[m][m]) % MO;
    84 
    85     printf("%d
    ", res);
    86     return 0;
    87 }
    AC代码

    CF1117E - Decypher the String

    题意:存在一个长为n的字符串和n个swap操作,但是你都不知道。你只知道操作的结果。你可以询问三次,每次给定一个字符串,回答该字符串操作的结果。输出原串。n <= 10000。

    解:可用的字符有26个。假如每次把集合s位置的字符变成a,那么操作后所有a的位置集合t和s之间就有一个双射。这样一次就可以把n / 26。发现26³ = 17576,刚好。

    实现上就是用一个结构体表示一组双射,两个vector存s和t。每次把这个vector分段染色,然后输出,操作完之后进行分组。

      1 #include <bits/stdc++.h>
      2 
      3 const int N = 10010;
      4 /// 26 * 26 = 676
      5 struct Node {
      6     std::vector<int> a, b;
      7 }stk[N]; int top;
      8 
      9 char str[N], my[N];
     10 int n;
     11 
     12 inline void solve(const Node &x) {
     13     int lm;
     14     if(x.a.size() <= 26) {
     15         lm = 1;
     16     }
     17     else if(x.a.size() <= 676) {
     18         lm = 26;
     19     }
     20     else {
     21         lm = 676;
     22     }
     23     int p = 0;
     24     char c = 'a';
     25     for(int i = 0; i < (int)x.a.size(); i++) {
     26         my[x.a[i]] = c;
     27         ++p;
     28         if(p == lm) {
     29             p = 0;
     30             ++c;
     31         }
     32     }
     33     return;
     34 }
     35 
     36 inline void work(int i) {
     37     int lm;
     38     if(stk[i].a.size() <= 26) {
     39         lm = 1;
     40     }
     41     else if(stk[i].a.size() <= 676) {
     42         lm = 26;
     43     }
     44     else {
     45         lm = 676;
     46     }
     47     int p = 0, cnt = 1, f;
     48     for(int j = 0; j < (int)stk[i].a.size(); j++) {
     49         stk[top + cnt].a.push_back(stk[i].a[j]);
     50         ++p;
     51         f = cnt;
     52         if(p == lm) {
     53             p = 0;
     54             ++cnt;
     55         }
     56     }
     57 
     58     for(int j = 0; j < (int)stk[i].a.size(); j++) {
     59         int x = stk[i].b[j];
     60         stk[top + 1 - 'a' + my[x]].b.push_back(x);
     61     }
     62     stk[i] = stk[top + f];
     63     stk[top + f].a.clear(); /// !!! important
     64     stk[top + f].b.clear(); ///
     65     top = top + f - 1;
     66     return;
     67 }
     68 
     69 int main() {
     70     scanf("%s", str + 1);
     71     n = strlen(str + 1);
     72 
     73     top = 1;
     74     for(int i = 1; i <= n; i++) {
     75         stk[1].a.push_back(i);
     76         stk[1].b.push_back(i);
     77         my[i] = 'a';
     78     }
     79 
     80     for(int t = 1; t <= 3; t++) {
     81         //printf("t = %d 
    ", t);
     82         for(int i = 1; i <= top; i++) {
     83             //printf("i = %d 
    ", i);
     84             if(stk[i].a.size() > 1) {
     85                 solve(stk[i]);
     86             }
     87         }
     88         printf("? ");
     89         for(int i = 1; i <= n; i++) {
     90             putchar(my[i]);
     91         }
     92         puts("");
     93         fflush(stdout);
     94         scanf("%s", my + 1);
     95         int temp = top;
     96         for(int i = 1; i <= temp; i++) {
     97             if(stk[i].a.size() > 1) {
     98                 work(i);
     99             }
    100         }
    101     }
    102 
    103     for(int i = 1; i <= n; i++) {
    104         my[stk[i].a[0]] = str[stk[i].b[0]];
    105     }
    106     printf("! ");
    107     for(int i = 1; i <= n; i++) {
    108         putchar(my[i]);
    109     }
    110     puts("");
    111     return 0;
    112 }
    AC代码

    这题还有一种CRT解法......

    具体来说,对每个位置i,考虑它是从哪来的,设从pos来。

    那么我们先询问三次,如下图。

    然后那么对于i来说,pos % 23 = s1[i] - 'a'。然后解方程即可。

    CF1146G Zoning Restrictions

    题意:你要在长为n的位置上填上[0, h]的数,如果填了x就会得到x2块钱。有m个限制,形如如果在[l, r]中有数超过了y,就要付c块钱。求最多钱数。n,m,h <= 50。

    解:有一种网络流做法是最小割。

    考虑切糕,把每个位置的取值都串起来,因为要最小,就先假设填了h,填x就是h2 - x2。对于限制,额外连向一个新点,向t连边为c。

      1 #include <bits/stdc++.h>
      2 
      3 const int N = 100010, INF = 0x3f3f3f3f;
      4 
      5 struct Edge {
      6     int nex, v, c;
      7     Edge(){}
      8     Edge(int N, int V, int C) {
      9         nex = N;
     10         v = V;
     11         c = C;
     12     }
     13 }edge[1000010]; int tp = 1;
     14 
     15 int e[N], n, h, m, vis[N], d[N];
     16 std::queue<int> Q;
     17 
     18 inline void add(int x, int y, int z) {
     19     edge[++tp] = Edge(e[x], y, z);
     20     e[x] = tp;
     21     edge[++tp] = Edge(e[y], x, 0);
     22     e[y] = tp;
     23     return;
     24 }
     25 
     26 inline bool BFS(int s, int t) {
     27     static int Time = 0;
     28     Q.push(s);
     29     ++Time;
     30     vis[s] = Time;
     31     d[s] = 0;
     32     while(Q.size()) {
     33         int x = Q.front();
     34         Q.pop();
     35         for(int i = e[x]; i; i = edge[i].nex) {
     36             int y = edge[i].v;
     37             if(vis[y] != Time && edge[i].c) {
     38                 vis[y] = Time;
     39                 d[y] = d[x] + 1;
     40                 Q.push(y);
     41             }
     42         }
     43     }
     44     return vis[t] == Time;
     45 }
     46 
     47 int DFS(int x, int t, int maxF) {
     48     if(x == t) {
     49         return maxF;
     50     }
     51     int ans = 0;
     52     for(int i = e[x]; i; i = edge[i].nex) {
     53         int y = edge[i].v;
     54         if(d[x] + 1 != d[y] || !edge[i].c) {
     55             continue;
     56         }
     57         int temp = DFS(y, t, std::min(maxF - ans, edge[i].c));
     58         if(!temp) {
     59             d[y] = INF;
     60         }
     61         ans += temp;
     62         edge[i].c -= temp;
     63         edge[i ^ 1].c += temp;
     64         if(ans == maxF) {
     65             break;
     66         }
     67     }
     68     return ans;
     69 }
     70 
     71 inline int solve(int s, int t) {
     72     int ans = 0;
     73     while(BFS(s, t)) {
     74         ans += DFS(s, t, INF);
     75     }
     76     return ans;
     77 }
     78 
     79 inline int id(int x, int y) {
     80     return (x - 1) * (h + 2) + y + 1;
     81 }
     82 
     83 int main() {
     84     
     85     scanf("%d%d%d", &n, &h, &m);
     86     int s = (h + 2) * n + m + 1, t = s + 1, lm = (h + 2) * n, V = h * h;
     87     for(int i = 1; i <= n; i++) {
     88         add(s, id(i, 0), INF);
     89         add(id(i, h + 1), t, INF);
     90         for(int j = 0; j <= h; j++) {
     91             add(id(i, j), id(i, j + 1), V - j * j);
     92         }
     93     }
     94     for(int i = 1; i <= m; i++) {
     95         int l, r, x, y;
     96         scanf("%d%d%d%d", &l, &r, &x, &y);
     97         if(x == h) continue;
     98         int p = lm + i;
     99         add(p, t, y);
    100         for(int j = l; j <= r; j++) {
    101             add(id(j, x + 1), p, INF);
    102         }
    103     }
    104     
    105     int ans = n * h * h;
    106     ans -= solve(s, t);
    107     printf("%d
    ", ans);
    108     return 0;
    109 }
    AC代码

    还有一种区间DP解法,我并不会...

    CF1147D - Palindrome XOR

    题意:求有多少对长度不超过n的无前导0的回文01串满足异或起来恰好为s。s的第一位一定是1,存在通配符。n <= 1000

    解:这个1000很有趣....可以O(n)枚举第二个串的开头在哪。

    然后发现这个回文和异或都是些形如“它们相等”或者“它们不等”的限制。于是考虑用并查集处理这些限制,每个连通块有两种取值。边带权并查集即可。

      1 #include <bits/stdc++.h>
      2 
      3 const int N = 1010, MO = 998244353;
      4 
      5 char str[N];
      6 int n;
      7 
      8 inline int qpow(int a, int b) {
      9     int ans = 1;
     10     while(b) {
     11         if(b & 1) ans = 1ll * ans * a % MO;
     12         a = 1ll * a * a % MO;
     13         b = b >> 1;
     14     }
     15     return ans;
     16 }
     17 
     18 namespace ufs {
     19     int cnt, fa[N * 2], val[N * 2];
     20     inline void init() {
     21         for(int i = 1; i <= n * 2 + 1; i++) {
     22             fa[i] = i;
     23             val[i] = 0;
     24         }
     25         cnt = 2 * n;
     26         return;
     27     }
     28     int find(int x, int &v) {
     29         if(x == fa[x]) {
     30             v = 0;
     31             return x;
     32         }
     33         int t = find(fa[x], v);
     34         v ^= val[x];
     35         fa[x] = t;
     36         val[x] = v;
     37         return t;
     38     }
     39     inline bool merge(int x, int y, int v) {
     40         int t, t2;
     41         x = find(x, t);
     42         y = find(y, t2);
     43         t ^= t2;
     44         if(x == y) {
     45             if(t != v) return false;
     46             return true;
     47         }
     48         else {
     49             fa[x] = y;
     50             val[x] = v ^ t;
     51             cnt--;
     52         }
     53         return true;
     54     }
     55 }
     56 using ufs::merge;
     57 
     58 int main() {
     59     scanf("%s", str + 1);
     60     n = strlen(str + 1);
     61     std::reverse(str + 1, str + n + 1);
     62     int ans = 0, Z = 2 * n + 1;
     63     for(int i = n - 1; i >= 1; i--) {
     64         ufs::init();
     65         if(!merge(n, Z, 1)) continue;
     66         if(!merge(1, Z, 1)) continue;
     67         if(!merge(n + 1, Z, 1)) continue;
     68         if(!merge(n + i, Z, 1)) continue;
     69         int fd = 0;
     70         for(int j = n; j > i; j--) {
     71             if(!merge(n + j, Z, 0)) {
     72                 fd = 1; break;
     73             }
     74         }
     75         if(fd) continue;
     76         for(int j = 1; j * 2 <= n; j++) {
     77             if(!merge(j, n - j + 1, 0)) {
     78                 fd = 1;
     79                 break;
     80             }
     81         }
     82         if(fd) continue;
     83         for(int j = 1; j * 2 <= i; j++) {
     84             if(!merge(n + j, n + i - j + 1, 0)) {
     85                 fd = 1;
     86                 break;
     87             }
     88         }
     89         if(fd) continue;
     90         for(int j = 1; j <= n; j++) {
     91             if(str[j] != '?') {
     92                 if(!merge(j, n + j, str[j] - '0')) {
     93                     fd = 1;
     94                     break;
     95                 }
     96             }
     97         }
     98         if(fd) continue;
     99         ans = (ans + qpow(2, ufs::cnt)) % MO;
    100     }
    101     printf("%d
    ", ans);
    102     return 0;
    103 }
    AC代码
  • 相关阅读:
    Constants and Variables
    随想
    C#基础篇之语言和框架介绍
    Python基础19 实例方法 类方法 静态方法 私有变量 私有方法 属性
    Python基础18 实例变量 类变量 构造方法
    Python基础17 嵌套函数 函数类型和Lambda表达式 三大基础函数 filter() map() reduce()
    Python基础16 函数返回值 作用区域 生成器
    Python基础11 List插入,删除,替换和其他常用方法 insert() remove() pop() reverse() copy() clear() index() count()
    Python基础15 函数的定义 使用关键字参数调用 参数默认值 可变参数
    Python基础14 字典的创建修改访问和遍历 popitem() keys() values() items()
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10761436.html
Copyright © 2011-2022 走看看