[关闭]
@sensitive-cs 2017-04-27T07:05:14.000000Z 字数 3615 阅读 760

校赛决赛题解

题解


补题第一题 切pizza:
思路:
赛上没有想到二分,用了线段树,可惜代码没有跑出来,当然,线段树其实是行不通的,因为是面积,除非二维线段树(2333,不知道有没有。后来,wj菊苣讲题解的时候说二分,因为美味度是随坐标单调增加的唉,然后又就没有听啦,当时太饿了。
然后,自己写的二分wa了十几遍过不了,很悲伤,是脑子除了问题,所以问了好人mz学长。
此题暴露出来的问题有:
1.二分判浮点精度的时候不能自己手写精度,一般这种要求精度的题用循环就可以了。
2.very important,m和n的范围是在1e5的范围内,所以相乘的话有可能会爆int,就是没想到,所以n*m的时候就直接乘在了1.0前面,后面读题时,第一是一定能要预判所有数据的最大范围,第二就是直接用long long或者double之类的就好了。mark一下,仔细一点。
代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <math.h>
  4. const double pi = acos(-1.0);
  5. struct node
  6. {
  7. double cen;
  8. double deg;
  9. double r;
  10. }node[100005];
  11. int n,m,k;
  12. double mm(double g)
  13. {
  14. double sum = 0;
  15. for (int i = 0;i < k;i++)
  16. {
  17. if (node[i].cen + node[i].r <= g)
  18. {
  19. sum += pi * node[i].r * node[i].r * node[i].deg;
  20. }
  21. else if (node[i].cen < g && node[i].cen + node[i].r > g)
  22. {
  23. double x = g - node[i].cen;
  24. double w = 2 * (acos(x / node[i].r));
  25. double area = pi * node[i].r * node[i].r - (1.0 / 2 * w * node[i].r * node[i].r - 1.0 / 2 * node[i].r * node[i].r * sin(w));
  26. sum += area * node[i].deg;
  27. }
  28. else if (node[i].cen - node[i].r < g && node[i].cen > g)
  29. {
  30. double x = node[i].cen - g;
  31. double w = 2 * (acos(x / node[i].r));
  32. double area = 1.0 / 2 * w * node[i].r * node[i].r - 1.0 / 2 * node[i].r * node[i].r * sin(w);
  33. sum += area * node[i].deg;
  34. }
  35. else if (node[i].cen == g)
  36. sum += 1.0 / 2 * pi * node[i].r * node[i].r;
  37. }
  38. return sum + m * g;
  39. }
  40. int main()
  41. {
  42. int t;
  43. scanf("%d",&t);
  44. while (t--)
  45. {
  46. double sum = 0;
  47. scanf("%d%d%d",&n,&m,&k);
  48. for (int i = 0;i < k;i++)
  49. {
  50. double t1;
  51. scanf("%lf%lf%lf%lf",&node[i].cen,&t1,&node[i].r,&node[i].deg);
  52. sum += pi * node[i].r * node[i].r * node[i].deg;
  53. }
  54. sum += 1.0 * n * m;
  55. double l = 0,h = n,mid;
  56. for (int i = 0;i < 100;i++)
  57. {
  58. mid = (l + h) / 2;
  59. if (mm(mid) >= sum / 2) h = mid;
  60. else l = mid;
  61. //printf("%f*****\n",mid);
  62. }
  63. printf("%.4f\n",mid);
  64. }
  65. return 0;
  66. }

补题第二题 24game:
这题在赛上写爆搜,兰儿wa了二十几发。赛下来写,我不是用的dfs。
第一种想法是枚举所有数字与符号的全排列,但是后来经过mzjj的验证有些情况是过不了的,并不能涵盖所有括号的情况,遂重新写。
后来去网上查了资料,发现了后缀表达式与表达式树,经过两节高数了的深思熟虑(滑稽,想出来了全排列与表达式树相结合的方法,1A了。
表达式树有三种:
1.(x op y)op(z op w);
2.(x op y) op z op w
3.x op y op (z op w)
主要是从 8 8 3 3这个想出来的

代码:

  1. #include <stdio.h>
  2. #include <math.h>
  3. #include <string.h>
  4. int a[4];
  5. bool v[4];
  6. int p[4];
  7. char b[1000][3];
  8. int cnt = 0;
  9. bool flag = 0;
  10. double cal(double x,int op,double y)
  11. {
  12. double ans;
  13. if (op == 0)
  14. ans = x + y;
  15. if (op == 1)
  16. ans = x - y;
  17. if (op == 2)
  18. ans = x * y;
  19. if (op == 3)
  20. {
  21. if (y == 0)
  22. ans = 100000000000;
  23. else
  24. ans = x / y;
  25. }
  26. return ans;
  27. }
  28. void dfs(void)
  29. {
  30. double c[4];
  31. for (int i = 0;i < 4;i++)
  32. c[i] = a[p[i]];
  33. for (int i = 0;i < cnt;i++)
  34. {
  35. if (flag) break;
  36. double ans;
  37. double x = cal(c[0],b[i][0],c[1]);
  38. double y = cal(c[2],b[i][2],c[3]);
  39. ans = cal(x,b[i][1],y);
  40. if (fabs(ans - 24) < 1e-8)
  41. flag = 1;
  42. ans = cal(c[0],b[i][0],c[1]);
  43. ans = cal(ans,b[i][1],c[2]);
  44. ans = cal(ans,b[i][2],c[3]);
  45. if (fabs(ans - 24) < 1e-8)
  46. flag = 1;
  47. ans = cal(c[0],b[i][0],c[1]);
  48. ans = cal(c[2],b[i][1],ans);
  49. ans = cal(c[3],b[i][2],ans);
  50. if (fabs(ans - 24) < 1e-8)
  51. flag = 1;
  52. }
  53. }
  54. void fun(int k)
  55. {
  56. if (flag) return;
  57. if (k == 4)
  58. dfs();
  59. for (int i = 0;i < 4;i++)
  60. {
  61. if (!v[i])
  62. {
  63. v[i] = 1;
  64. p[k] = i;
  65. fun(k+1);
  66. v[i] = 0;
  67. }
  68. }
  69. }
  70. int main()
  71. {
  72. int t;
  73. scanf("%d",&t);
  74. for (int i = 0;i < 4;i++)
  75. for (int j = 0;j < 4;j++)
  76. for (int k = 0;k < 4;k++)
  77. {
  78. b[cnt][0] = i;
  79. b[cnt][1] = j;
  80. b[cnt][2] = k;
  81. cnt++;
  82. }
  83. while (t--)
  84. {
  85. flag = 0;
  86. memset(v,0,sizeof(v));
  87. memset(p,0,sizeof(p));
  88. for (int i = 0;i < 4;i++)
  89. scanf("%d",&a[i]);
  90. fun(0);
  91. if (flag)
  92. printf("Yes\n");
  93. else
  94. printf("No\n");
  95. }
  96. return 0;
  97. }

第三题 马(无敌js
思路:首先可以推出马可以到达的坐标,横纵坐标之和必须是3的倍数,其次,此坐标必须满足杨辉三角,即abs(n-m) <= (n + m) / 3。之后,就是大组合数取模的问题了。
求解这个问题,主要用到了几个定理。
第一,Lucas定理:

C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p

mark 一下
第二 费马小定理:
a的逆元就是a的p-2次方
第三:
依据费马小定理,计算逆元时需要用到快速幂算法。

这题主要是学习新姿势。

代码:

  1. #include <stdio.h>
  2. typedef long long ll;
  3. const ll p = 1000003;
  4. int abs(int x)
  5. {
  6. if (x < 0)
  7. return -x;
  8. else
  9. return x;
  10. }
  11. ll quick_mod(ll a,ll b)
  12. {
  13. if (b == 0) return 1;
  14. ll x = quick_mod(a,b / 2);
  15. ll ans = x * x % p;
  16. if (b % 2) ans = ans * a % p;
  17. return ans;
  18. }
  19. ll C(ll n, ll m)
  20. {
  21. if (m > n) return 0;
  22. ll ans = 1;
  23. for (int i = 1;i <= m;i++)
  24. {
  25. ll a = n - m + i;
  26. ll b = i;
  27. ans = ans * (a * quick_mod(b,(p - 2)) % p) % p;
  28. }
  29. return ans;
  30. }
  31. ll Lucas(ll n,ll m)
  32. {
  33. if (m == 0)
  34. return 1;
  35. else
  36. return C(n % p,m % p) * Lucas(n / p,m / p) % p;
  37. }
  38. int main()
  39. {
  40. int t;
  41. scanf("%d",&t);
  42. while (t--)
  43. {
  44. int n,m;
  45. scanf("%d%d",&n,&m);
  46. n--;m--;
  47. if ((m + n) % 3 == 0)
  48. {
  49. if (abs(n-m) > (n + m) / 3)
  50. printf("0\n");
  51. else
  52. printf("%lld\n",Lucas((n + m) / 3,m - (n + m) / 3));
  53. }
  54. else
  55. printf("0\n");
  56. }
  57. return 0;
  58. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注