2024ICPC网络赛第一场

惊雷无声  金牌会员 | 2024-9-25 03:46:09 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 826|帖子 826|积分 2478

VP链接:https://qoj.ac/contest/1794
A. World Cup

最终答案与中国队本领值的排名有关,具体每个情况手推一下,用 if else 即可通过。


  • 如果输出 1,中国队是冠军,那么本领值一定第 1。
  • 如果输出 2,中国队是亚军,那么只能在决赛被打败排名一定在前 5,至少 27 支队伍比中国队弱(8强中另一个半区的4 支队伍都可以比中国队强)。
  • 如果输出 4,中国队是 4 强,那么就是在半决赛被打败,即在 1 / 4 和 1 / 8 决赛中得胜。中国队从 1 / 8 中得胜那么说明中国队至少强于 6 支队伍,中国队在 1 / 4 中的对手也强于 6 支队伍(具体原因看 4)。中国又比 1 / 4 决赛中的对手强,那么中国队至少强于 13 支队伍。
  • 如果输出 8,中国队是 8 强,那么是在 1 / 4 决赛被打败,即只需在 1 / 8 中得胜。如果中国队以小组第一出线,那么1 / 8 决赛对手地点组至少 3 队比中国队弱;如果中国队以小组第二出线,那么对手地点组全队伍伍都比中国队弱。以上两种情况都是至少有 6 支队伍比中国队弱。
  • 如果输出 16,中国队是 16 强,那么是在 1 / 8 决赛被打败。中国队只必要保证小组出线,以是至少 2 队比中国队弱。
  • 如果输出 32,中国队是 32 强,那么小组没出线,最多只有 1 支队伍比中国队弱。
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int main() {
  4.     ios::sync_with_stdio(false); cin.tie(0);
  5.     int t, a[40];
  6.     cin >> t;
  7.     while (t--) {
  8.         int num = 0;
  9.         for (int i = 0; i < 32; i++) {
  10.             cin >> a[i];
  11.             if (a[i] <= a[0]) num++;
  12.         }
  13.         if (num == 32) puts("1");
  14.         else if (num >= 28) puts("2");
  15.         else if (num >= 14) puts("4");
  16.         else if (num >= 7) puts("8");
  17.         else if (num >= 3) puts("16");
  18.         else puts("32");
  19.     }
  20.     return 0;
  21. }
复制代码
C. Permutation Counting 4

问题可以等价于由全部边 
 构成的图是不是一棵树。
假设有一个矩阵 
 ,问题可以等价于求 A 矩阵的行列式 det(A):方案数为奇数,det(A) 不等于零;方案数为偶数,det(A) = 0。
对于这个矩阵可以在上边和左边分别补上一行一列,而且除了左上角是 1,剩余都是 0。然后从前去后依次执行前一列减后一列的利用,那么对于每一行 
,都有 
,剩余位置都是 0。
如果 
 这些边成环,成环的边对应的矩阵的行一定是线性相干的,即行列式为 0。否则根据行列式界说睁开,一定会有一项是非零的,行列式不为零。
可以借助并查集来简单判断图中是否有环。
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6 + 10;
  4. int t, n, f[N];
  5. int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
  6. int main() {
  7.         ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
  8.         cin >> t;
  9.         while (t--) {
  10.                 cin >> n;
  11.                 for (int i = 0; i <= n; i++) f[i] = i;
  12.                 int ok = 1;
  13.                 for (int i = 1; i <= n; i++) {
  14.                         int l, r;
  15.                         cin >> l >> r;
  16.                         if (find(l - 1) == find(r)) ok = 0;
  17.                         else f[find(l - 1)] = find(r);
  18.                 }
  19.                 cout << ok << '\n';
  20.         }
  21.         return 0;
  22. }
复制代码
F. Make Max

每一个数都能更新左右比它小的数,直到遇到一个大于等于它的数为止。从小到大一个个更新,更新次数一定最多,但在计算结果的时间只必要计算数目,不用考虑计算顺序。
利用单调栈求出每一个 
 左边第一个大于等于它的数的位置 
 和右边第一个大于等于它的数的位置 

计算结果的时间要注意,如果 
,即
 与左边第一个大于等于它的数相称的时间,只必要记右边小于它的数的个数,不然会跟前面的算重;别的情况都要加上左右小于它的数的个数。
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 2e5 + 10;
  4. int n, a[N], lef[N], rig[N];
  5. stack<int> s;
  6. inline int read() {
  7.     int x = 0, f = 1; char c = getchar();
  8.     while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
  9.     while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
  10.     return x * f;
  11. }
  12. int main () {
  13.     int t = read();
  14.     while (t--) {
  15.         n = read();
  16.         for (int i = 1; i <= n; i++) a[i] = read();
  17.         for (int i = 1; i <= n; i++) {
  18.             if (s.empty() || a[i] < a[s.top()]) s.push(i);
  19.             else {
  20.                 while (!s.empty() && a[i] >= a[s.top()]) {
  21.                     rig[s.top()] = i;
  22.                     s.pop();
  23.                 }
  24.                 s.push(i);
  25.             }
  26.         }
  27.         while (!s.empty()) rig[s.top()] = n + 1, s.pop();
  28.         for (int i = n; i; i--) {
  29.             if (s.empty() || a[i] < a[s.top()]) s.push(i);
  30.             else {
  31.                 while (!s.empty() && a[i] >= a[s.top()]) {
  32.                     lef[s.top()] = i;
  33.                     s.pop();
  34.                 }
  35.                 s.push(i);
  36.             }
  37.         }
  38.         while (!s.empty()) lef[s.top()] = 0, s.pop();
  39.         long long ans = 0LL;
  40.         for (int i = 1; i <= n; i++) {
  41.             if (!lef[i] || a[lef[i]] != a[i]) ans += rig[i] - lef[i] - 2;
  42.             else ans += rig[i] - i - 1;
  43.         }
  44.         printf("%lld\n", ans);
  45.     }
  46.     return 0;
  47. }
复制代码
G. The Median of the Median of the Median

总体思路:先二分最终的中位数,再判断现实中位数是否大于等于二分的中位数
利用 suma 数组记录 a 大于等于二分的中位数 x 的数目的前缀和。
 记录 a 数组该区间内的中位数是否大于等于 x(如果 a 数组该区间内的中位数是大于等于 x的,那么至少有一半,即 
 个 a 要大于等于 x)。
 同样记录对应区间内的中位数是否大于等于 x 且原理与 b 相同。
最后判断 c 数组中是否有凌驾一半的数是大于等于二分的中位数的。
c 数组的前缀和处理(根据样例 1 举例说明):1 3 1 7
b1234
11111
2313
311
47
c1234
11111
2311
311
47
以上是按照界说,b,c 数组对应区间内的中位数,两个数组存有数据的部分都类似于一个倒三角形。
可以发现原界说的 
 是 
 这个倒三角区域内全部的数中位数,这个倒三角恰好是以 (i,j)为右上角的倒三角。以是代码中 c 数组的每一个 
 记录的是:在原界说的 b 数组中,以(i,j)为右上角的倒三角区域内,大于等于二分的中位数x的数目。
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 2010;
  4. int n, suma[N], a[N], b[N][N], c[N][N];
  5. inline int read() {
  6.         int x = 0, f = 1; char c = getchar();
  7.         while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
  8.         while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
  9.         return x * f;
  10. }
  11. bool check(int x) {
  12.         for (int i = 1; i <= n; i++) suma[i] = suma[i - 1] + (a[i] >= x);
  13.     // 记录大于等于 x 的 a[i] 的数量
  14.         for (int i = 1; i <= n; i++)
  15.                 for (int j = i; j <= n; j++)
  16.                         c[i][j] = b[i][j] = (2 * (suma[j] - suma[i - 1]) > (j - i + 1));
  17.         for (int i = 1; i <= n; i++)
  18.                 for (int j = i + 1; j <= n; j++) c[i][j] += c[i][j - 1];
  19.     // 统计 b 数组倒三角左边区域大于等于 x 的数量
  20.         for (int j = 1; j <= n; j++)
  21.                 for (int i = j - 1; i >= 1; i--) c[i][j] += c[i + 1][j];
  22.     // 统计 b 数组倒三角下面区域大于等于 x 的数量
  23.         int sumc = 0;
  24.         for (int i = 1; i <= n; i++)
  25.                 for (int j = i; j <= n; j++)
  26.                         sumc += (2 * c[i][j]) > (j - i + 1) * (j - i + 2) / 2;
  27.     // (j - i + 1) * (j - i + 2) / 2 是对应区间内 b 数量的一半
  28.         return 2 * sumc > n * (n + 1) / 2; // n * (n + 1) / 2 就是 c 数量的一半
  29. }
  30. int main() {
  31.         n = read();
  32.         for (int i = 1; i <= n; i++) a[i] = read();
  33.         int l = 0, r = 1e9 + 1;
  34.         while (l <= r) {
  35.                 int mid = l + r >> 1;
  36.                 if (check(mid)) l = mid;
  37.                 else r = mid;
  38.         } // 二分中位数
  39.         printf("%d\n", l);
  40.         return 0;
  41. }
复制代码
M. Find The Easiest Problem

用 set 记录每一个题目通过的队伍的名字,最后比力队伍数目。
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int main() {
  4.     ios::sync_with_stdio(false); cin.tie(0);
  5.     int t, n;
  6.     string team, status;
  7.     char prob;
  8.     cin >> t;
  9.     while (t--) {
  10.         cin >> n;
  11.         set<string> st[30];
  12.         for (int i = 1; i <= n; i++) {
  13.             cin >> team >> prob >> status;
  14.             if (status == "accepted") st[prob - 'A'].insert(team);
  15.         }
  16.         int idx = 0;
  17.         for (int i = 1; i <= 'Z' - 'A'; i++) {
  18.             if (st[idx].size() < st[i].size()) idx = i;
  19.         }
  20.         printf("%c\n", 'A' + idx);
  21.     }
  22.     return 0;
  23. }
复制代码



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表