• 牛客OI周赛15-普及组


    参考文献:

      https://ac.nowcoder.com/discuss/400201?type=101&order=0&pos=9&page=1

      https://www.acwing.com/video/125/

    A题 咪咪游戏https://ac.nowcoder.com/acm/contest/4911/A

      签到题。

      算出长度,显然奇数不行,然后遍历一遍,奇数位为'm',偶数位为'q'。(下标从1开始)

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e5 + 10;
     4 
     5 char s[N];
     6 
     7 int main()
     8 {
     9     int t; cin >> t;
    10     while(t --)
    11     {
    12         scanf("%s", s + 1);
    13         int len_s = strlen(s + 1);
    14         int mark = 1;
    15         if(len_s & 1)   mark = 0;
    16         for(int i = 1; i <= len_s; i ++)
    17         {
    18             if(i & 1)
    19             {
    20                 if(s[i] == 'm') continue;
    21                 else { mark = 0; break; }
    22             }
    23             else
    24             {
    25                 if(s[i] == 'q') continue;
    26                 else { mark = 0; break; }
    27             }
    28         }
    29         if(mark) puts("Yes");
    30         else puts("No");
    31     }
    32     return 0;
    33 }
    View Code

    B题 三角形https://ac.nowcoder.com/acm/contest/4911/B

      原题:https://www.acwing.com/problem/content/148/

      思路:

        M个序列里分别选一个数,我们先从第一行和第二行来合并出新行,再用新行与第三合并,依次类推,最后的新行的前K个数的即为答案。

        设数组A为第一行,数组B为第二行。合并的为C数组

        先将A从小到大排序,那么我可以得到的新数组为:

        A【1】+ B【1】, A【1】+ B【2】,A【1】+ B【3】...... A【1】+ B【M【B】】(M【B】为B的个数)

        A【2】+ B【1】, A【2】+ B【2】,A【2】+ B【3】...... A【2】+ B【M【B】】

        A【3】+ B【1】, A【3】+ B【2】,A【3】+ B【3】...... A【3】+ B【M【B】】

        A【4】+ B【1】, A【4】+ B【2】,A【4】+ B【3】...... A【4】+ B【M【B】】

        ......

        A【M【A】】+ B【1】,...... 共M【A】* M【B】个

        显然每列的第一个数为每列的最小值

        我们的目的是求前K个最小值,所以我们C数组的长度为 min( K, M【A】* M【A】);

        对应合并我们需要的操作是求最小值,插入新的值和删除,显然优先队列可以满足我们的条件,先将第一行放入优先队列,并加一个下标标记,对于取出的最小值,就插入他对应的下一行。直到取到K个值就可以了。总共合并N - 1 次即可。

     1 #include <cstdio>
     2 #include <queue>
     3 #include <algorithm>
     4 #include <vector>
     5 #include <iostream>
     6 using namespace std;
     7 const int N = 110, K = 1e4 + 10;
     8 typedef pair<int, int> PII;
     9 
    10 int n, k, f[N][N]; // f 为 第 i 行 的 宝物值
    11 int a[K], b[K], c[K], cnt[N]; // cnt 为 每一行的宝物个数
    12 int len; // len是 a 数组的长度
    13 
    14 void merge(int ma, int mb) // ma 是 a 数组的长度; mb 是数组 b 的长度
    15 {
    16     priority_queue<PII, vector<PII>, greater<PII> > heap; // 小根堆
    17     for(int i = 1; i <= mb; i ++) heap.push({a[1] + b[i], 1}); // 插入第一行
    18     for(int i = 1; i <= min(k, ma * mb); i ++)
    19     {
    20         auto t = heap.top(); 
    21         heap.pop();
    22         int s = t.first, p = t.second;
    23         c[i] = s;
    24         // s - a[p] + a[p + 1] = a[p] + b[?]  - a[p] + a[p + 1] = a[p + 1] + b[?];
    25         if(p + 1 <= ma) heap.push({s - a[p] + a[p + 1], p + 1});
    26     }
    27 
    28     for(int i = 1; i <= min(k, ma * mb); i ++) a[i] = c[i]; // 再放入 a 数组
    29     len = min(k, ma * mb);  // 更新 a 数组长度
    30 }
    31 
    32 int main()
    33 {
    34     cin >> n >> k;
    35     for(int i = 1; i <= n; i++)
    36     {
    37         int m; scanf("%d", &m);
    38         for(int j = 1; j <= m; j ++)    scanf("%d", &f[i][j]);
    39         cnt[i] = m;
    40     }
    41 
    42     for(int i = 1; i <= cnt[1]; i ++)   a[i] = f[1][i]; // 得到 a 数组
    43     sort(a + 1, a + cnt[1] + 1);    // 对 a 排序
    44     len = cnt[1];   // 记录 a 数组的长度
    45     for(int i = 2; i <= n; i ++) // n - 1 次合并
    46     {
    47         for(int j = 1; j <= cnt[i]; j ++)   b[j] = f[i][j]; // 得到 b 数组
    48         merge(len, cnt[i]); // 合并
    49     }
    50 
    51     long long ans = 0;
    52     for(int i = 1; i <= k; i ++)    ans += a[i];
    53     cout << ans << endl;
    54 
    55     return 0;
    56 }
    View Code

    C题 区间加https://ac.nowcoder.com/acm/contest/4911/C

      思路:

      着实坑,首先要知道第一个数如果不是 m - 1 或 m ,答案就为 0;因为第一个数只能加 1 次。

      可以类比为括号,左括号表示操作区间的左端点,右括号表示操作区间的右端点,则一个位置不能出现两个及以上的左括号或右括号。

      那么对于任意一个位置都有四种情况:

        1. 没有括号;

        2.只有左括号;

        3.只有右括号;

        4.同时包含左括号和右括号。

      那么我们可以设 f [ i ] [ j ] 为前 i 个位置,左括号比右括号多  j 个情况下并且 [1, i] 的区间中任意一个数等于 m 的方案数。

      对于不放括号:f [ i ] [ j ] += f [ i - 1] [ j ];

      对于只有左括号:f [ i ] [ j ] += f [ i - 1] [ j - 1];

      对于只有右括号:f [ i ] [ j ] += ( j + 1)  *  f [ i - 1] [ j + 1];

              (在第 i 个位置放了右括号后,左括号还比右括号多 j 个,则 [ 1, i - 1]里左括号比右括号多 j + 1 个。乘上 j + 1 是因为给这个右括号可以匹配的左括号有 j + 1 个);

      对于同时包含左括号和右括号:f [ i ] [ j ] += ( j + 1)  *  f [ i - 1] [ j ] ; (道理同上);

      其中情况 1 和 情况 2 ,a [ i ] 都加 了 j 

          情况 3 和 情况 4 , a [ i ] 都加了 j - 1

      最后答案即为 f [ n ] [ 0 ];

      

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int MOD = 998244355, N = 2e3 + 10;
     4 typedef long long ll;
     5 
     6 int n, m, a[N];
     7 ll f[N][N];
     8 
     9 int main()
    10 {
    11     cin >> n >> m;
    12     for(int i = 1; i <= n; i ++)    scanf("%d", &a[i]);
    13 
    14     // 初始化 如果不是这两种情况,即为 0;
    15     if(a[1] == m || a[1] == m - 1) f[1][0] = 1;
    16     if(a[1] == m - 1) f[1][1] = 1;
    17 
    18     for(int i = 2; i <= n; i ++)
    19         for(int j = max(0, m - a[i] - 1); j <= max(i, m - a[i]); j ++)
    20         {
    21             if(a[i] + j == m)
    22             {
    23                 f[i][j] = (f[i][j] + f[i - 1][j]) % MOD;
    24                 if(j) f[i][j] = (f[i][j] + f[i - 1][j - 1]) % MOD;
    25             }
    26 
    27             if(a[i] + j + 1 == m)
    28             {
    29                 f[i][j] = (f[i][j] + ll(j + 1) * f[i - 1][j + 1]) % MOD;
    30                 f[i][j] = (f[i][j] + ll(j + 1) * f[i - 1][j]) % MOD;
    31             }
    32         }
    33     
    34    // f[n][0] = (f[n][0] + MOD) % MOD;
    35     
    36     cout << f[n][0] << endl;
    37 
    38     
    39 
    40     return 0;
    41 }
    View Code

    D 题  多元组https://ac.nowcoder.com/acm/contest/4911/D

      思路:

      对于三元组的情况,任意一个数的贡献是 左边比他小的 *  右边比他大的,我们是用树状数解决的, 所以这道题就是在三元组上面的拓展;

      先可以得到递推式:$f[i][j] = sumlimits_{k=1}^{i-1}f[k][j-1](a_k<a_i)$

      显然我们至少需要 M 个 树状数组用来求和。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 using namespace std;
     6 const int N = 1e5 + 10, M = 60, MOD = 1e9 + 7;
     7 typedef long long ll;
     8 
     9 int n, m;
    10 int a[N], b[N], f[N][M], tr[M][N];
    11 
    12 int lowbit(int x) { return x & -x; }
    13 
    14 ll query(int *tr, int x)
    15 {
    16     ll res = 0;
    17     while(x)
    18     {
    19         res += tr[x];
    20         x -= lowbit(x);
    21         res %= MOD;
    22     }
    23     return res;
    24 }
    25 
    26 void add(int *tr, int x, int c)
    27 {
    28     while(x <= n)
    29     {
    30         tr[x] += c;
    31         tr[x] %= MOD;
    32         x += lowbit(x);
    33     }
    34 }
    35 
    36 
    37 int main()
    38 {
    39     cin >> n >> m;
    40     for(int i = 1; i <= n; i ++) { scanf("%d", &a[i]); b[i] = a[i]; }
    41     sort(b + 1, b + n + 1);
    42     int len_b = unique(b + 1, b + n + 1) - b - 1;
    43     for(int i = 1; i <= n; i ++)
    44         a[i] = lower_bound(b + 1, b + len_b + 1, a[i]) - b;
    45 
    46     for(int i = 1; i <= n; i ++) f[i][1] = 1;
    47 
    48     ll ans = 0;
    49     for(int i = 1; i <= n; i ++)
    50     {
    51         for(int j = 2; j <= m; j ++)
    52             f[i][j] = query(tr[j - 1], a[i] - 1); // a[i]-1即是小与a[i]的
    53         for(int j = 1; j <= m; j ++)
    54             add(tr[j], a[i], f[i][j]);
    55         ans += f[i][m];
    56         ans %= MOD;
    57     }
    58 
    59     cout << ans << endl;
    60 
    61     return 0;
    62 }
    View Code
  • 相关阅读:
    CentOS 6.4 系统下的MySQL的主从库配置
    扫盲: JAVA基本常识
    ant学习
    Linux一些命令
    redis学习
    扫盲:注册表和绿色软件常识
    Java.前端.Layer.open.btn验证无效
    Java.数据结构.集合体系详解
    PageHelper踩坑
    Scrum.站立会议介绍
  • 原文地址:https://www.cnblogs.com/nonameless/p/12631760.html
Copyright © 2020-2023  润新知