牛客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
原文地址:https://www.cnblogs.com/nonameless/p/12631760.html