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代码
  • 相关阅读:
    "SoftwareHex-RaysIDA" exists, but no "Python3TargetDLL" value found No Python installations were found
    visual studio修改安装位置
    ror13hash
    IMAGE_DIRECTORY_ENTRY_SECURITY【证书属性表
    华为云自动学习物体检测(滑动窗口)
    Java BigInteger中的oddModPow
    vue 动态路由传参三种方式
    git根据项目地址使用不同代理服务器
    关于MySQL8的WITH查询学习
    有1、2、3、4个数字,能组成多少个互不相同 且无重复数字的三位数?都是多少?
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10761436.html
Copyright © 2011-2022 走看看