[关闭]
@Dmaxiya 2026-03-09T06:18:03.000000Z 字数 8446 阅读 924

2018 Multi-University Training Contest 6

暑期集训


链接:2018 Multi-University Training Contest 6
过题数:3
排名:135/718
成员:官展鹏,冯彦博,孙昊哲

A. oval-and-rectangle

题意

在椭圆 上,取一个矩形,要求满足以下条件:

  1. 矩形的四个点在椭圆上;
  2. 矩形的两条边平行于坐标轴;
  3. 矩形的一条边为

可能为 内随机的一个点,求矩形周长的期望值。

输入

第一行为一个整数 ,接下去有 行,每行两个整数 ,用一个空白符分隔。

输出

对于每组数据,输出一个实数,只取小数点后 位,多余位数直接舍去。

题解

表示矩形的周长后,对 积分,将积分后的结果除以 就是答案。

其中 ,令 则有:

因此

过题代码

  1. #include <cstdio>
  2. #include <cmath>
  3. using namespace std;
  4. typedef long long LL;
  5. double PI = acos(-1.0);
  6. int T;
  7. double a, b, ans;
  8. int main() {
  9. #ifdef ExRoc
  10. freopen("test.txt", "r", stdin);
  11. #endif // ExRoc
  12. scanf("%d", &T);
  13. while (T--) {
  14. scanf("%lf%lf", &a, &b);
  15. ans = PI * a + 2 * b;
  16. ans = floor(ans * 1000000) / 1000000;
  17. printf("%.6f\n", ans);
  18. }
  19. return 0;
  20. }

B. bookshelf

题意

本完全相同的书要放到一个有 层的书架上,每层书架可以放无限本书:

  1. 设第 层的书的数量为
  2. 为斐波那契数列的第 项();
  3. 层的稳定值为
  4. 层的美丽值为
  5. 书架整体的美丽值为 ,其中

如果现在随机地把 本书全部放到 层书架上,求 的期望。

输入

第一行为一个整数 ,接下去 行,每行两个整数

输出

对每组数据输出期望值对 取模的结果,如果期望值为有理数 都为整数且它们互质,则输出一个 以内的整数 使得 能被 整除。

题解

本完全相同的书分到 层的总方案数为

由斐波那契数列性质可得:,因此只要统计每一个可能的 值及对应的方案数相乘即可。将 本书放到书架上所有 的可能值为 的所有约数,对于某一个 的值,其方案数如果直接用 来算的话,会将 的所有倍数重复计算(若每一层的 都是 ,则该方案数会在统计 时被计算一次,在统计 时又被统计一次),为减去重复的统计,应该用容斥将所有是 的约数且又是 倍数 的方案数除去,方案数为 ,系数为莫比乌斯函数 ,因此答案为:

过题代码

  1. #include <cstdio>
  2. #include <vector>
  3. #include <algorithm>
  4. using namespace std;
  5. typedef long long LL;
  6. const int MOD = 1000000007;
  7. const int phi_MOD = MOD - 1;
  8. const int maxn = 2000000 + 100;
  9. int T, n, k, cnt;
  10. int prime[maxn], mu[maxn], fib[maxn];
  11. bool vis[maxn];
  12. int inv[maxn], pro[maxn], invpro[maxn];
  13. vector<int> fac;
  14. void Prepare_C() {
  15. inv[1] = 1;
  16. for (int i = 2; i < maxn; ++i) {
  17. inv[i] = (LL)(MOD - MOD / i) * inv[MOD % i] % MOD;
  18. }
  19. pro[0] = invpro[0] = 1;
  20. for (int i = 1; i < maxn; ++i) {
  21. pro[i] = (LL)pro[i - 1] * i % MOD;
  22. invpro[i] = (LL)invpro[i - 1] * inv[i] % MOD;
  23. }
  24. }
  25. int get_C(int n, int m) {
  26. if (n < m) {
  27. return 0;
  28. }
  29. return (LL)pro[n] * invpro[m] % MOD * invpro[n - m] % MOD;
  30. }
  31. void Prime(int n) {
  32. mu[1] = 1;
  33. for (int i = 2; i <= n; ++i) {
  34. if (!vis[i]) {
  35. prime[cnt++] = i;
  36. mu[i] = -1;
  37. }
  38. for (int j = 0; j < cnt && i <= n / prime[j]; ++j) {
  39. int k = i * prime[j];
  40. vis[k] = true;
  41. if (i % prime[j] == 0) {
  42. mu[k] = 0;
  43. break;
  44. } else {
  45. mu[k] = -mu[i];
  46. }
  47. }
  48. }
  49. }
  50. int fast_pow(LL res, LL n) {
  51. LL ans;
  52. for (ans = 1; n != 0; n >>= 1) {
  53. if ((n & 1) == 1) {
  54. ans = (ans * res) % MOD;
  55. }
  56. res = (res * res) % MOD;
  57. }
  58. return ans;
  59. }
  60. int get_inv(int x) {
  61. return fast_pow(x, MOD - 2);
  62. }
  63. inline int add(int a, int b, int m) {
  64. a += b;
  65. if (a >= m) {
  66. return a - m;
  67. }
  68. if (a < 0) {
  69. return a + m;
  70. }
  71. return a;
  72. }
  73. void Prepaer_fib() {
  74. bool flag = false;
  75. int Index = 0;
  76. fib[0] = 0;
  77. fib[1] = 1;
  78. for (int i = 2; i < maxn; ++i) {
  79. if (flag) {
  80. fib[i] = add(fib[i - 1], fib[i - 2], phi_MOD);
  81. } else {
  82. fib[i] = fib[i - 1] + fib[i - 2];
  83. if (fib[i] > phi_MOD) {
  84. flag = true;
  85. Index = i;
  86. fib[i] -= phi_MOD;
  87. }
  88. }
  89. }
  90. for (int i = Index; i < maxn; ++i) {
  91. fib[i] += phi_MOD;
  92. }
  93. }
  94. int main() {
  95. #ifdef ExRoc
  96. freopen("test.txt", "r", stdin);
  97. #endif // ExRoc
  98. Prime(maxn - 1);
  99. Prepare_C();
  100. Prepaer_fib();
  101. scanf("%d", &T);
  102. while (T--) {
  103. fac.clear();
  104. scanf("%d%d", &n, &k);
  105. for (int i = 1; i <= n / i; ++i) {
  106. if (n % i == 0) {
  107. fac.push_back(i);
  108. if (i != n / i) {
  109. fac.push_back(n / i);
  110. }
  111. }
  112. }
  113. sort(fac.begin(), fac.end());
  114. int len = fac.size();
  115. LL ans = 0;
  116. for (int i = 0; i < len; ++i) {
  117. int tmp = add(fast_pow(2, fib[fac[i]]), -1, MOD);
  118. int x = 0;
  119. for (int j = i; j < len; ++j) {
  120. if (fac[j] % fac[i] == 0) {
  121. x = add(x, get_C(n / fac[j] + k - 1, n / fac[j]) * mu[fac[j] / fac[i]], MOD);
  122. }
  123. }
  124. ans = add(ans, (LL)tmp * x % MOD, MOD);
  125. }
  126. printf("%d\n", (int)(ans * get_inv(get_C(n + k - 1, n)) % MOD));
  127. }
  128. return 0;
  129. }

D. Shoot Game

题意

在一个平面直角坐标系上有 个障碍物,第 个障碍物用三个整数 描述,表示这个障碍物在高度为 处,左端点为 ,右端点为 ,它的防御力为 ,一个人在 处发射激光,每次可以选择一个能量 的激光进行发射,激光可以穿透障碍物,所有防御力不大于激光能量的障碍物都会被消灭,问要消灭所有障碍物,最少需要消耗多少能量。

输入

第一行为一个整数 ,接下去为 组数据,每组数据第一行为一个整数 ,接下去 行每行 个整数

输出

对于每组数据,输出最少需要小号的能量值。

题解

处发射激光,则可以将所有端点按极角序进行离散化,这样一条射线可以同时射中的障碍物就转化为在一个一维坐标轴上的相互覆盖的线段了,用 表示要将从第 个端点到第 个端点内所有线段消灭所需要的最小能量,则有 方程:

其中 为从端点 到端点 之间所有线段中需要的最大能量。

过题代码

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <vector>
  4. #include <algorithm>
  5. using namespace std;
  6. typedef long long LL;
  7. const int maxn = 600 + 10;
  8. struct Point {
  9. LL x, y;
  10. Point() {}
  11. Point(LL xx, LL yy) {
  12. x = xx;
  13. y = yy;
  14. }
  15. };
  16. LL operator^(const Point &a, const Point &b) {
  17. return a.x * b.y - a.y * b.x;
  18. }
  19. bool operator<(const Point &a, const Point &b) {
  20. return (a ^ b) < 0;
  21. }
  22. bool operator==(const Point &a, const Point &b) {
  23. return (a ^ b) == 0;
  24. }
  25. struct Line {
  26. LL l, r, h, w;
  27. };
  28. int T, n;
  29. LL INF;
  30. vector<Point> sand;
  31. Line line[maxn];
  32. LL dp[maxn][maxn];
  33. int main() {
  34. #ifdef ExRoc
  35. freopen("test.txt", "r", stdin);
  36. #endif // ExRoc
  37. memset(&INF, 0x3f, sizeof(LL));
  38. scanf("%d", &T);
  39. while (T--) {
  40. sand.clear();
  41. scanf("%d", &n);
  42. for (int i = 1; i <= n; ++i) {
  43. scanf("%I64d%I64d%I64d%I64d", &line[i].h, &line[i].l, &line[i].r, &line[i].w);
  44. sand.push_back(Point(line[i].l, line[i].h));
  45. sand.push_back(Point(line[i].r, line[i].h));
  46. }
  47. sort(sand.begin(), sand.end());
  48. sand.erase(unique(sand.begin(), sand.end()), sand.end());
  49. for (int i = 1; i <= n; ++i) {
  50. line[i].l = lower_bound(sand.begin(), sand.end(), Point(line[i].l, line[i].h)) - sand.begin() + 1;
  51. line[i].r = lower_bound(sand.begin(), sand.end(), Point(line[i].r, line[i].h)) - sand.begin() + 1;
  52. }
  53. for (int i = 1; i <= 2 * n; ++i) {
  54. memset(dp[i], 0, sizeof(LL) * (i + 1));
  55. }
  56. for (int len = 1; len <= 2 * n; ++len) {
  57. for (int i = 1; i + len <= 2 * n; ++i) {
  58. int j = i + len;
  59. int Max = 0;
  60. for (int k = 1; k <= n; ++k) {
  61. if (line[k].l < i || line[k].r > j) {
  62. continue;
  63. }
  64. if (Max < line[k].w) {
  65. Max = line[k].w;
  66. }
  67. }
  68. dp[i][j] = INF;
  69. for (int k = i; k <= j; ++k) {
  70. dp[i][j] = min(dp[i][j], dp[i][k - 1] + dp[k + 1][j] + Max);
  71. }
  72. }
  73. }
  74. printf("%I64d\n", dp[1][2 * n]);
  75. }
  76. return 0;
  77. }

I. Werewolf

题意

个人玩一个狼人游戏,每个人的身份只可能是“村民”或者“狼人”,如果一个人是村民,那么他只能说真话,如果他是狼人,则他可以说真话也可以说假话,现在每个人都要指认另一个人的身份,问有多少人的身份可以确定是村民,有多少人的身份可以确定是狼人。

输入

第一行为一个整数 ,接下去有 组数据,每组数据第 行为一个整数 ,接下去有 行,第 行为一个整数 与一个字符串 ,表示第 个人指认第 个人的身份为村民 / 狼人。

输出

对于每组数据,依次输出一定为村民的玩家数量,与一定为狼人的玩家数量。

题解

不论什么情况下,所有人都有可能为狼人,因此一定为村民的玩家数量等于 ,现在分析一定为狼人的玩家数量。如果 指认 为村民,则从 连一条有向的村民边,如果 指认 为狼人,则从 连一条有向的狼人边,将所有以村民边相连的加入到同一个连通块内,如果是一个 字形的连通块,则连通块内所有玩家都有可能是村民。如果是一棵树(若 有一条村民边指向 ,则将 作为 的父节点),则狼人边一定是从树根连出的(因为所有点的出度都为 ,只有树根的出度为 ),假设树根为狼人,则所有指向树根的节点都是狼人,以此类推则整棵树都是狼人,假设树根为村民,则树根指向的节点是狼人,则以该节点为父节点的整颗子树上的节点都是狼人,因此两种假设下,一定为狼人的节点就是树根指向的节点所代表的子树。

过题代码

  1. #include <cstdio>
  2. #include <vector>
  3. using namespace std;
  4. typedef long long LL;
  5. const int maxn = 100000 + 100;
  6. int T, n;
  7. int pos;
  8. char str[20];
  9. int fa[maxn], son[maxn];
  10. bool vis[maxn];
  11. vector<int> G[maxn];
  12. vector<pair<int, int> > edge;
  13. void Init() {
  14. edge.clear();
  15. for (int i = 1; i <= n; ++i) {
  16. fa[i] = i;
  17. vis[i] = false;
  18. G[i].clear();
  19. }
  20. }
  21. int Find(int x) {
  22. return x == fa[x]? x: fa[x] = Find(fa[x]);
  23. }
  24. void unit(int x, int y) {
  25. int xx = Find(x);
  26. int yy = Find(y);
  27. fa[xx] = yy;
  28. }
  29. void dfs(int x) {
  30. if (vis[x]) {
  31. return ;
  32. }
  33. vis[x] = true;
  34. son[x] = 1;
  35. int len = G[x].size();
  36. for (int i = 0; i < len; ++i) {
  37. int pos = G[x][i];
  38. dfs(pos);
  39. son[x] += son[pos];
  40. }
  41. }
  42. int main() {
  43. #ifdef ExRoc
  44. freopen("test.txt", "r", stdin);
  45. #endif // ExRoc
  46. scanf("%d", &T);
  47. while (T--) {
  48. scanf("%d", &n);
  49. Init();
  50. for (int i = 1; i <= n; ++i) {
  51. scanf("%d %s", &pos, str);
  52. if (str[0] == 'w') {
  53. edge.push_back(make_pair(i, pos));
  54. } else {
  55. unit(i, pos);
  56. G[pos].push_back(i);
  57. }
  58. }
  59. for (int i = 1; i <= n; ++i) {
  60. if (!vis[Find(i)]) {
  61. dfs(Find(i));
  62. }
  63. }
  64. int ans = 0;
  65. int len = edge.size();
  66. for (int i = 0; i < len; ++i) {
  67. int u = edge[i].first;
  68. int v = edge[i].second;
  69. if (Find(u) == Find(v)) {
  70. ans += son[v];
  71. }
  72. }
  73. printf("0 %d\n", ans);
  74. }
  75. return 0;
  76. }

L. Pinball

题意

在以下平面直角坐标系中,有一经过点 的斜面,斜面底部在原点,在斜面上方 处有一小球做自由落体运动,在碰到斜面后小球做完全弹性碰撞,碰撞过程无能量损失,重力加速度为 ,数据保证小球最少撞到斜面上一次。

问在小球离开斜面之前,将与斜面发生多少次碰撞。

输入

第一行为一个整数 ,接下去有 组数据,每组数据为四个整数 ,含义如题。

输出

输出答案,数据保证答案不超过

题解

将速度按与斜面平行与垂直方向分解,则在与斜面平行方向上,小球在做匀加速直线运动,且加速度为 ,在垂直于斜面方向上,第一次接触斜面之前做自由落体运动,在接触到斜面之后做周期性的上抛运动(加速度均为 )。

可以先计算出小球在沿平行于斜面方向上运动的时间 ,第一次接触斜面之前的时间 与上抛运动的周期 ,则小球与斜面碰撞的次数为:

过题代码

  1. #include <cstdio>
  2. #include <cmath>
  3. using namespace std;
  4. typedef long long LL;
  5. const double g = 9.8;
  6. int T;
  7. double a, b, c, d;
  8. int main() {
  9. #ifdef ExRoc
  10. freopen("test.txt", "r", stdin);
  11. #endif // ExRoc
  12. scanf("%d", &T);
  13. while (T--) {
  14. scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
  15. c = -c;
  16. double e = (a * b * d + a * a * c) / (a * a + b * b);
  17. double f = (b * b * d + a * b * c) / (a * a + b * b);
  18. double sx = sqrt(e * e + f * f);
  19. double ax = g * b / sqrt(a * a + b * b);
  20. double tx = sqrt(2 * sx / ax);
  21. double ay = g * a / sqrt(a * a + b * b);
  22. double sy = sqrt((c - e) * (c - e) + (d - f) * (d - f));
  23. double ty1 = sqrt(2 * sy / ay);
  24. double vy = ay * ty1;
  25. double ty2 = vy / ay * 2;
  26. int ans = (tx - ty1) / ty2 + 1;
  27. printf("%d\n", ans);
  28. }
  29. return 0;
  30. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注