[关闭]
@Dmaxiya 2026-03-09T06:21:00.000000Z 字数 7495 阅读 6

2018 Multi-University Training Contest 7

暑期集训


链接:2018 Multi-University Training Contest 7
过题数:4
排名:87
成员:官展鹏,冯彦博,孙昊哲

A. Age of Moyu

题意

在一张 个节点 条边的无向图上,每条边的权值为 ,求从节点 到节点 的最小代价路径,每当路径经过的边权值发生改变时,代价加

输入

多组输入,第一行为两个整数 ,接下去 行每行三个整数 ,表示节点 之间有一条边,边的权值为 ,输入到文件尾。

输出

对于每组数据,输出最小权值,如果无法从 到达 ,输出

题解

用一个 set 保存从节点 到当前节点所有代价最小的路径中的前置边权值集合,到遍历节点的下一条边时,从集合中寻找是否已经有匹配的边,如果有则按照迪杰斯特拉进行最短路的更新。

过题代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <vector>
  4. #include <set>
  5. #include <queue>
  6. using namespace std;
  7. typedef long long LL;
  8. const int SIZE = 100000;
  9. const int maxn = 100000 + 100;
  10. inline char nc() {
  11. static char buf[SIZE];
  12. static char *p1 = buf;
  13. static char *p2 = buf;
  14. return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, SIZE, stdin), p1 == p2)? EOF : *p1++;
  15. }
  16. inline int _read() {
  17. char ch = nc();
  18. if (ch == EOF) {
  19. return EOF;
  20. }
  21. int sum = 0;
  22. while (!(ch >= '0' && ch <= '9')) {
  23. ch = nc();
  24. }
  25. while (ch >= '0' && ch <= '9') {
  26. sum = sum * 10 + ch - 48;
  27. ch = nc();
  28. }
  29. return sum;
  30. }
  31. struct Node {
  32. int pos, w;
  33. Node() {}
  34. Node(int p, int ww) {
  35. pos = p;
  36. w = ww;
  37. }
  38. };
  39. struct Edge {
  40. int pos, dis, w;
  41. Edge() {}
  42. Edge(int p, int d, int ww) {
  43. pos = p;
  44. dis = d;
  45. w = ww;
  46. }
  47. };
  48. bool operator<(const Edge &a, const Edge &b) {
  49. return a.dis > b.dis;
  50. }
  51. int n, m, u, v, w, INF;
  52. int dis[maxn];
  53. set<int> st[maxn];
  54. vector<Node> G[maxn];
  55. priority_queue<Edge> que;
  56. void dij() {
  57. que.push(Edge(1, 0, -1));
  58. st[1].insert(-1);
  59. dis[1] = 0;
  60. while (!que.empty()) {
  61. Edge tmp = que.top();
  62. que.pop();
  63. int len = G[tmp.pos].size();
  64. for (int i = 0; i < len; ++i) {
  65. int pos = G[tmp.pos][i].pos;
  66. int w = G[tmp.pos][i].w;
  67. int d = (w == tmp.w? 0 : 1);
  68. if (dis[pos] > tmp.dis + d) {
  69. dis[pos] = tmp.dis + d;
  70. que.push(Edge(pos, dis[pos], w));
  71. st[pos].clear();
  72. st[pos].insert(w);
  73. } else if (dis[pos] == tmp.dis + d && st[pos].find(w) == st[pos].end()) {
  74. st[pos].insert(w);
  75. que.push(Edge(pos, dis[pos], w));
  76. }
  77. }
  78. }
  79. }
  80. int main() {
  81. memset(&INF, 0x3f, sizeof(int));
  82. while (n = _read(), n != EOF) {
  83. m = _read();
  84. for (int i = 1; i <= n; ++i) {
  85. dis[i] = INF;
  86. G[i].clear();
  87. st[i].clear();
  88. }
  89. for (int i = 0; i < m; ++i) {
  90. u = _read();
  91. v = _read();
  92. w = _read();
  93. G[u].push_back(Node(v, w));
  94. G[v].push_back(Node(u, w));
  95. }
  96. dij();
  97. if (dis[n] == INF) {
  98. printf("-1\n");
  99. continue;
  100. }
  101. printf("%d\n", dis[n]);
  102. }
  103. return 0;
  104. }

E. GuGuFishtion

题意

给定 ,求

输入

第一行一个正整数 ,接下去 行每行 个整数 ,数据保证 为质数,含义如题。

输出

输出 行,每行对应一组输入的答案。

题解

根据欧拉公式可得分子为:


其中 表示是 的质因数但不是 的质因数的整数集合,共 个元素, 表示是 的质因数但不是 的质因数的整数集合,共 个元素, 表示同时属于 的质因数的整数集合,共 个。

分母相乘得:


约去分子分母相同元素,再同时乘上 可得:

故原式等价于

右式等价于统计 中最大公因数为 的个数,可用莫比乌斯反演求出。

题目保证质数比 大,故直接求逆元最后乘起来即可。

过题代码

  1. #include <cstdio>
  2. #include <algorithm>
  3. using namespace std;
  4. typedef long long LL;
  5. const int maxn = 1000000 + 100;
  6. int T, n, m, MOD, cnt, ub;
  7. bool vis[maxn];
  8. LL inv[maxn], ans;
  9. int prime[maxn], mu[maxn], phi[maxn];
  10. void Prime(int n) {
  11. mu[1] = 1;
  12. phi[1] = 1;
  13. for (LL i = 2; i < n; ++i) {
  14. if (!vis[i]) {
  15. prime[cnt++] = i;
  16. mu[i] = -1;
  17. phi[i] = i - 1;
  18. }
  19. for (int j = 0; j < cnt && i * prime[j] < n; ++j) {
  20. int k = i * prime[j];
  21. vis[k] = true;
  22. if (i % prime[j] == 0) {
  23. mu[k] = 0;
  24. phi[k] = phi[i] * prime[j];
  25. break;
  26. } else {
  27. mu[k] = -mu[i];
  28. phi[k] = phi[i] * (prime[j] - 1);
  29. }
  30. }
  31. }
  32. }
  33. LL solve(int Gcd) {
  34. int nn = n / Gcd;
  35. int mm = m / Gcd;
  36. int ub = min(nn, mm);
  37. LL ans = 0;
  38. for (int k = 1; k <= ub; ++k) {
  39. ans += (LL)mu[k] * (nn / k) * (mm / k);
  40. }
  41. return ans;
  42. }
  43. LL gcd(LL x, LL y) {
  44. return y == 0? x: gcd(y, x % y);
  45. }
  46. int main() {
  47. #ifdef ExRoc
  48. freopen("test.txt", "r", stdin);
  49. #endif // ExRoc
  50. Prime(maxn);
  51. scanf("%d", &T);
  52. while (T--) {
  53. ans = 0;
  54. scanf("%d%d%d", &n, &m, &MOD);
  55. ub = min(n, m);
  56. inv[1] = 1;
  57. for (int i = 2; i <= ub; ++i) {
  58. inv[i] = (LL)(MOD - MOD / i) * inv[MOD % i] % MOD;
  59. }
  60. for (int i = 1; i <= ub; ++i) {
  61. ans += (solve(i) * i % MOD) * inv[phi[i]] % MOD;
  62. ans %= MOD;
  63. }
  64. printf("%I64d\n", ans);
  65. }
  66. return 0;
  67. }

J. Sequence

题意

给定 ,求

取模的结果

输入

第一行一个正整数 ,接下去 行每行 个整数 ,含义如题。

输出

输出 行,每行对应一组输入的答案。

题解

不同的取值在 级别,然后按此分段用矩乘快速幂递推,注意 的大小。

过题代码

  1. #include <cstdio>
  2. #include <cstring>
  3. using namespace std;
  4. typedef long long LL;
  5. const int maxn = 100000 + 100;
  6. const int MOD = 1000000007;
  7. int T, n;
  8. LL P, A, B, C, D, f1, f2, f3;
  9. LL add(LL a, LL b) {
  10. a += b;
  11. if (a >= MOD) {
  12. return a - MOD;
  13. }
  14. return a;
  15. }
  16. struct Matrix {
  17. LL num[3][3];
  18. void Init() {
  19. memset(num, 0, sizeof(num));
  20. for (int i = 0; i < 3; ++i) {
  21. num[i][i] = 1;
  22. }
  23. }
  24. void Set() {
  25. memset(num, 0, sizeof(num));
  26. num[0][0] = D;
  27. num[0][1] = C;
  28. num[0][2] = 1;
  29. num[1][0] = 1;
  30. num[2][2] = 1;
  31. }
  32. void operator*=(const Matrix &m) {
  33. static Matrix ans;
  34. for (int i = 0; i < 3; ++i) {
  35. for (int j = 0; j < 3; ++j) {
  36. ans.num[i][j] = 0;
  37. for (int k = 0; k < 3; ++k) {
  38. ans.num[i][j] = add(ans.num[i][j], num[i][k] * m.num[k][j] % MOD);
  39. }
  40. }
  41. }
  42. memcpy(num, ans.num, sizeof(num));
  43. }
  44. void fast_pow(LL n) {
  45. static Matrix ans;
  46. for (ans.Init(); n != 0; n >>= 1) {
  47. if ((n & 1) == 1) {
  48. ans *= (*this);
  49. }
  50. (*this) *= (*this);
  51. }
  52. memcpy(num, ans.num, sizeof(num));
  53. }
  54. } m;
  55. LL For(int n) {
  56. for (int i = 3; i <= n; ++i) {
  57. f3 = add(add(C * f1 % MOD, D * f2 % MOD), P / i);
  58. f1 = f2;
  59. f2 = f3;
  60. }
  61. return f3;
  62. }
  63. bool judge(LL x, LL mid) {
  64. return P / mid == P / x;
  65. }
  66. LL Bsearch(LL x) {
  67. LL low = x;
  68. LL high = n + 1;
  69. LL mid;
  70. while (high - low > 1) {
  71. mid = (high + low) >> 1;
  72. if (judge(x, mid)) {
  73. low = mid;
  74. } else {
  75. high = mid;
  76. }
  77. }
  78. return low;
  79. }
  80. int main() {
  81. #ifdef ExRoc
  82. freopen("test.txt", "r", stdin);
  83. #endif // ExRoc
  84. scanf("%d", &T);
  85. while (T--) {
  86. scanf("%I64d%I64d%I64d%I64d%I64d%d", &A, &B, &C, &D, &P, &n);
  87. f1 = A;
  88. f2 = B;
  89. f3 = 0;
  90. if (n <= 100000) {
  91. printf("%I64d\n", For(n));
  92. continue;
  93. }
  94. For(100000);
  95. LL x = 100000;
  96. LL xx;
  97. while (x != n) {
  98. xx = Bsearch(x + 1);
  99. m.Set();
  100. m.fast_pow(xx - x);
  101. f3 = add(m.num[0][0] * f2 % MOD, add(m.num[0][1] * f1 % MOD, m.num[0][2] * (P / xx) % MOD));
  102. f2 = add(add(m.num[1][0] * f2 % MOD, m.num[1][1] * f1 % MOD), m.num[1][2] * (P / xx) % MOD);
  103. f1 = f2;
  104. f2 = f3;
  105. x = xx;
  106. }
  107. printf("%I64d\n", f3);
  108. }
  109. return 0;
  110. }

K. Swordsman

题意

Lawson 有 个魔法属性 ,总共有 只怪物,每只怪物也有 个属性 ,只有满足 时,Lawson 才能打败怪物,并且将自己的所有属性值增加为 ,每只怪物的 个属性及被打败后增加的属性值各不相同,问 Lawson 最多能打败多少只怪物,且最终 个属性的值为多少。

输入

第一行一个正整数 ,接下去 组数据,每组数据第一行为两个整数 ,表示 只怪物与 个属性,第二行为 个非负整数 表示 Lawson 的 个属性,接下去 行,每行 个非负整数 ,含义如题。

数据保证所有输入数据都不超过 ,且

数据规模很大,注意使用快速读入。

输出

每组数据两行输出,第一行一个整数表示 Lawson 最多能杀死的怪物数,第二行为 个整数 表示 Lawson 最终的属性值。

题解

以每个属性为关键字从小到大排序,然后使用 个指针从小到大扫描,是否能打败对应的怪物,最多扫描 次。

过题代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. typedef long long LL;
  6. const int maxn = 100000 + 100;
  7. const int maxk = 5;
  8. int T, n, k, iidx, cnt;
  9. int v[maxk];
  10. int a[maxn][maxk], b[maxn][maxk];
  11. int idx[maxk][maxn], ed[maxk];
  12. bool vis[maxn];
  13. int read() {
  14. int ret = 0;
  15. char ch;
  16. do {
  17. ch = getchar();
  18. } while (ch < '0' || ch > '9');
  19. do {
  20. ret = ret * 10 + ch - '0';
  21. ch = getchar();
  22. } while (ch >= '0' && ch <= '9');
  23. return ret;
  24. }
  25. bool cmp(int x, int y) {
  26. return a[x][iidx] < a[y][iidx];
  27. }
  28. bool judge(int idx) {
  29. for (int i = 0; i < k; ++i) {
  30. if (v[i] < a[idx][i]) {
  31. return false;
  32. }
  33. }
  34. return true;
  35. }
  36. int main() {
  37. #ifdef ExRoc
  38. freopen("test.txt", "r", stdin);
  39. #endif // ExRoc
  40. T = read();
  41. while (T--) {
  42. cnt = 0;
  43. n = read();
  44. k = read();
  45. for (int i = 0; i < k; ++i) {
  46. v[i] = read();
  47. }
  48. memset(vis, 0, sizeof(bool) * n);
  49. for (int i = 0; i < n; ++i) {
  50. for (int j = 0; j < k; ++j) {
  51. a[i][j] = read();
  52. }
  53. for (int j = 0; j < k; ++j) {
  54. b[i][j] = read();
  55. idx[j][i] = i;
  56. }
  57. }
  58. for (int i = 0; i < k; ++i) {
  59. iidx = i;
  60. sort(idx[i], idx[i] + n, cmp);
  61. }
  62. memset(ed, 0, sizeof(int) * k);
  63. for (int i = 0; i < n; ++i) {
  64. for (int j = 0; j < k; ++j) {
  65. iidx = idx[j][ed[j]];
  66. if (vis[iidx]) {
  67. ++ed[j];
  68. continue;
  69. }
  70. if (judge(iidx)) {
  71. for (int kk = 0; kk < k; ++kk) {
  72. v[kk] += b[iidx][kk];
  73. }
  74. vis[iidx] = true;
  75. ++ed[j];
  76. ++cnt;
  77. }
  78. }
  79. }
  80. printf("%d\n", cnt);
  81. for (int i = 0; i < k; ++i) {
  82. if (i != 0) {
  83. printf(" ");
  84. }
  85. printf("%d", v[i]);
  86. }
  87. printf("\n");
  88. }
  89. return 0;
  90. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注