[关闭]
@wsndy-xx 2019-08-18T11:37:21.000000Z 字数 5183 阅读 651

Noip 2019 冲刺


多做几套模拟题,体会心路历程。


from hzwer 模拟赛整理 2014-10-5 NOIP模拟赛
time 08.17
忘记做过多长时间了,应该不到 3h

T1 祖孙询问

已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。
对于30%的数据,n,m≤1000。
对于100%的.据,n,m≤40000,每个节点的编号都不超过40000。

算法分析:存在祖孙关系的话二者的 Lca 一定为其中之一, 求 Lca 判断即可

得分分析:放在一年以前,20min AC 不在话下,现在的码力确实大减啊。
这种难度的题目就是给我这种水平的选手提供的吧。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <cstring>
  7. using namespace std;
  8. const int N = 5e4 + 10;
  9. #define gc getchar()
  10. inline int read() {
  11. int x = 0, f = 1; char c = gc;
  12. while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc;}
  13. while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
  14. return x * f;
  15. }
  16. int head[N], now = 1;
  17. int n;
  18. struct Node {
  19. int v, nxt;
  20. } G[N << 1];
  21. int fa[N], son[N], size[N], topp[N], deep[N];
  22. int root;
  23. void Add(int u, int v) {
  24. G[now].v = v; G[now].nxt = head[u]; head[u] = now ++;
  25. }
  26. void Dfs1(int u, int f, int dep) {
  27. fa[u] = f, deep[u] = dep; size[u] = 1;
  28. for(int i = head[u]; ~ i; i = G[i].nxt) {
  29. int v = G[i].v;
  30. if(v != f) {
  31. Dfs1(v, u, dep + 1);
  32. size[u] += size[v];
  33. if(size[son[u]] < size[v]) son[u] = v;
  34. }
  35. }
  36. }
  37. void Dfs2(int u, int tp) {
  38. topp[u] = tp;
  39. if(!son[u]) return ;
  40. Dfs2(son[u], tp);
  41. for(int i = head[u]; ~ i; i = G[i].nxt) {
  42. int v = G[i].v;
  43. if(v != fa[u] && v != son[u]) {
  44. Dfs2(v, v);
  45. }
  46. }
  47. }
  48. int Lca(int x, int y) {
  49. int tpx = topp[x], tpy = topp[y];
  50. while(tpx != tpy) {
  51. if(deep[tpx] < deep[tpy]) swap(x, y), swap(tpx, tpy);
  52. x = fa[tpx];
  53. tpx = topp[x];
  54. }
  55. if(deep[x] < deep[y]) swap(x, y);
  56. return y;
  57. }
  58. int main() {
  59. freopen("tree.in", "r", stdin);
  60. freopen("tree.out", "w", stdout);
  61. n = read();
  62. for(int i = 1; i <= N - 5; i ++) head[i] = -1;
  63. for(int i = 1; i <= n; i ++) {
  64. int u = read(), v = read();
  65. if(v == -1) root = u;
  66. else Add(u, v), Add(v, u);
  67. }
  68. Dfs1(root, 0, 0);
  69. Dfs2(root, root);
  70. int q = read();
  71. for( ; q; q --) {
  72. int x = read(), y = read();
  73. int lca = Lca(x, y);
  74. if(lca == x) {
  75. puts("1");
  76. } else if(lca == y) {
  77. puts("2");
  78. } else {
  79. puts("0");
  80. }
  81. }
  82. return 0;
  83. }

T2 比赛

【问题描述】
有两个队伍A和B,每个队伍都有n个人。这两支队伍之间进行n场1对1比赛,每一场都是由A中的一个选手与B中的一个选手对抗。同一个人不会参加多场比赛,每个人的对手都是随机而等概率的。例如A队有A1和A2两个人,B队有B1和B2两个人,那么(A1 vs B1,A2 vs B2)和(A1 vs B2,A2 vs B1)的概率都是均等的50%。
每个选手都有一个非负的实力值。如果实力值为X和Y的选手对抗,那么实力值较强的选手所在的队伍将会获得(X-Y)^2的得分。
求A的得分减B的得分的期望值。
【输入格式】
第一行一个数n表示两队的人数为n。
第二行n个数,第i个数A[i]表示队伍A的第i个人的实力值。
第三行n个数,第i个数B[i]表示队伍B的第i个人的实力值。
【输出格式】
输出仅包含一个实数表示A期望赢B多少分。答案保留到小数点后一位(注意精度)。
【样例输入】
2
3 7
1 5
【样例输出】
20.0
【数据规模】
对于30%的数据,n≤50。
对于100%的.据,n≤50000;A[i],B[i]≤50000

算法分析:根据期望的和=和的期望,只需把A队每个人的期望得分减去B队每个人的期望得分即为答案。
两个人相遇的概率为 (1/n),
假设固定 B 的选手不动,对 A 进行排列会存在 (n!) 种情况,其中 A 的选手 a 与 B 的选手 b 相遇的情况有 (n - 1) ! 种,所以相遇的概率为 (1/n)
对于贡献,排序后可以线性算出,lower_bound 也可以,只不过算价值时复杂度带 log,不过无所谓啦。

得分分析:这道题一开始想的是暴力枚举所有的情况然后计算,不过部分分不允许这样做,后来想到了正解的做法,只不过两者相遇的概率想错了,后来‘偷了’两份大样例然后乱试一番找到了正确的概率,写了出来,不过卡精度这种事虽然经常听说但是我还是第一次遇到。。。

代码:

  1. #include <iostream>
  2. #include <string>
  3. #include <cstdio>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <cstdlib>
  7. using namespace std;
  8. const int N = 5e4 + 10;
  9. double a[N], b[N];
  10. int n;
  11. double f[N], sum[N], sumf[N];
  12. int main() {
  13. freopen("mat.in", "r", stdin);
  14. freopen("mat.out", "w", stdout);
  15. scanf("%d", &n);
  16. for(int i = 1; i <= n; i ++) scanf("%lf", &a[i]);
  17. for(int i = 1; i <= n; i ++) scanf("%lf", &b[i]);
  18. sort(a + 1, a + n + 1);
  19. sort(b + 1, b + n + 1);
  20. for(int i = 1; i <= n; i ++) f[i] = b[i] * b[i];
  21. for(int i = 1; i <= n; i ++) sumf[i] = sumf[i - 1] + f[i];
  22. for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + b[i];
  23. double Ansa = 0;
  24. for(int i = 1; i <= n; i ++) {
  25. int wei = lower_bound(b, b + n + 1, a[i]) - b;
  26. wei --;
  27. if(wei < 1) continue;
  28. double js = (double) wei;
  29. Ansa += (js * a[i] * a[i] - 2 * sum[wei] * a[i] + sumf[wei]);
  30. }
  31. for(int i = 1; i <= n; i ++) f[i] = a[i] * a[i];
  32. for(int i = 1; i <= n; i ++) sumf[i] = sumf[i - 1] + f[i];
  33. for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + a[i];
  34. double Ansb = 0;
  35. for(int i = 1; i <= n; i ++) {
  36. int wei = lower_bound(a, a + n + 1, b[i]) - a;
  37. wei --;
  38. if(wei < 1) continue;
  39. double js = (double) wei;
  40. Ansb += (js * b[i] * b[i] - 2 * sum[wei] * b[i] + sumf[wei]);
  41. }
  42. double nn = n * 1.0;
  43. double Answer = (Ansa - Ansb) / nn;
  44. printf("%.1lf", Answer);
  45. return 0;
  46. }

T3 数字

【问题描述】
一个数字被称为好数字当他满足下列条件:
1. 它有2*n个数位,n是正整数(允许有前导0)。
2. 构成它的每个数字都在给定的数字集合S中。
3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等
例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。
已知n,求合法的好数字的个数mod 999983。
【输入格式】
第一行一个数n。
接下来一个长度不超过10的字符串,表示给定的数字集合。
【输出格式】
一行一个数字表示合法的好数字的个数mod 999983。
【样例输入】
2
0987654321
【样例输出】
1240
【数据规模】
对于20%的数据,n≤7。
对于100%的.据,n≤1000,|S|≤10。

算法分析:
ANS=前n位之和与后n位之和相等的方案数+奇数位之和与偶数位之和相等的方案数-前n位之和与后n位之和相等且奇数位之和与偶数位之和相等的方案数
前2个需要+的方案数都很好求,直接递推,重点是最后一个要满足2个条件的方案数怎么求,其实也很简单:
因为前n位之和=后n位之和,奇数位之和=偶数位之和
所以前n位中奇数位之和=后n位中偶数位之和 且
前n位中偶数位之和=后n位中奇数位之和
现在只要求上面这个问题的方案数,由于两个等式中的元素无交集,也是十分好算的。

得分分析:这题不给暴力分我怎么得分呢?

代码:

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <iomanip>
  4. #include <functional>
  5. #include <fstream>
  6. #include <cstring>
  7. #include <cstdio>
  8. #include <cstdlib>
  9. using namespace std;
  10. const int maxn = 1000 + 50;
  11. const int maxsum = 10000 + 50;
  12. const int mo = 999983;
  13. ifstream fin("num.in");
  14. ofstream fout("num.out");
  15. int f[maxn][maxsum] = {0};
  16. int n;
  17. bool canuse[10] = {0};
  18. int maxvalue = 0;
  19. long long ans = 0;
  20. void ReadData() {
  21. fin >> n;
  22. char c;
  23. while (fin >> c) {
  24. canuse[c - '0'] = true;
  25. maxvalue = max(maxvalue, c - '0');
  26. }
  27. }
  28. void DP() {
  29. f[0][0] = 1;
  30. for (int i = 0; i <= maxvalue; i++) {
  31. if (canuse[i]) {
  32. f[1][i] = 1;
  33. }
  34. }
  35. for (int i = 1; i < n; i++) {
  36. for (int j = 0; j <= maxvalue * i; j++) {
  37. for (int k = 0; k <= maxvalue; k++) {
  38. if (canuse[k]) {
  39. f[i + 1][j + k] += f[i][j];
  40. f[i + 1][j + k] %= mo;
  41. }
  42. }
  43. }
  44. }
  45. }
  46. inline long long sqr(long long X) {
  47. return X * X;
  48. }
  49. void CalcAns() {
  50. for (int i = 0; i <= maxvalue * n; i++) {
  51. ans += 2 * sqr(f[n][i]);
  52. ans %= mo;
  53. }
  54. int a = int(n / 2.0 - 1e-8) + 1;
  55. int b = n >> 1;
  56. long long A = 0;
  57. long long B = 0;
  58. for (int i = 0; i <= maxvalue * a; i++) {
  59. A += sqr(f[a][i]);
  60. A %= mo;
  61. }
  62. for (int i = 0; i <= maxvalue * b; i++) {
  63. B += sqr(f[b][i]);
  64. B %= mo;
  65. }
  66. ans = (ans + mo - A * B % mo) % mo;
  67. }
  68. int main() {
  69. ReadData();
  70. DP();
  71. CalcAns();
  72. fout << ans << endl;
  73. return 0;
  74. }

总结:个人感觉这套题目的难度比较接近 Noip2018 Day1 (noip2019 没了, upset)。不过对于第三题的评价个人无法完成。
对于 T2 如果有大样例,并且不存在卡精度的数据的话可以在 90min 内解决这道题目,但是这题卡精度能得多少分这就只能看命了。
20min + 90min + 0min = 110min 的时间内得到 100 + 60(应该是100啊) + 0 = 160分 的成绩。水平有限。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注