');}

9.27模拟赛题解

9.27模拟赛题解

相遇

相遇.jpg

树上路径求交模版题。

Binary_tree

由于一共有 n 个点与 n - 1 条边,所以便隐晦地告诉我们这张图没有环。

要看 x_1, x_2, y_1, y_2 是否有公共路径,我们可以分别求出 p = LCA(x_1, y_1) 以及 q = LCA(x_2, y_2) ,如果 p = q 的话,那么两个路径都经过一个相同的点,直接输出 YES 就可以了。

那么,就像上面这张图,当 x_1 = 2, y_1 = 11, x_2 = 5, y_2 = 11 的话,虽然有相同的路径,但是 p = 7 q = 6 ,这应该怎么办呢?我们就可以判断 LCA(p, x2) LCA(p, y2) 的值是否等于 q 。反之当 q p 上方时同理求出。

  1. #include <queue>
  2. #include <string>
  3. #include <cstdio>
  4. #include <cstring>
  5. #include <algorithm>
  6. #define maxx 1000001
  7. int n, m, s;
  8. int cnt, head[maxx];
  9. struct node{
  10. int to, next;
  11. } e[maxx];
  12. void add_edge(int x, int y) {
  13. cnt++;
  14. e[cnt].to = y;
  15. e[cnt].next = head[x];
  16. head[x] = cnt;
  17. }
  18. int deep[maxx], father[maxx][21], log[maxx];
  19. void DFS(int now, int fa) {
  20. father[now][0] = fa;
  21. deep[now] = deep[fa] + 1;
  22. for (int i = 1; i <= log[deep[now]]; i++) {
  23. father[now][i] = father[father[now][i - 1]][i - 1];
  24. }
  25. for (int i = head[now]; i; i = e[i].next) {
  26. if (e[i].to != fa)
  27. DFS(e[i].to, now);
  28. }
  29. }
  30. int LCA(int x, int y) {
  31. if (deep[x] < deep[y]) std::swap(x, y); //使 x 一直处于下面, y 一直处于上面
  32. for (int i = 20; i >= 0; i--) {
  33. if (deep[father[x][i]] >= deep[y])
  34. x = father[x][i];
  35. if (x == y) return x;
  36. }
  37. for (int i = 20; i >= 0; i--) {
  38. if (father[x][i] != father[y][i]) {
  39. x = father[x][i];
  40. y = father[y][i];
  41. }
  42. }
  43. return father[x][0];
  44. }
  45. int main() {
  46. //freopen("railway.in", "r", stdin);
  47. //freopen("railway.out", "w", stdout);
  48. int T;
  49. scanf("%d", &T);
  50. while(T--) {
  51. scanf("%d %d", &n, &m);
  52. for (int i = 1; i <= n - 1; i++) {
  53. int x, y;
  54. scanf("%d %d", &x, &y);
  55. add_edge(x, y);
  56. add_edge(y, x);
  57. }
  58. for (int i = 1; i <= n; i++) {
  59. log[i] = log[i - 1] + (1 << log[i - 1] == i);
  60. }
  61. s = 1;
  62. DFS(s, 0);
  63. for (int i = 1; i <= m; i++) {
  64. int x1, y1, x2, y2;
  65. scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
  66. int p = LCA(x1, y1), q = LCA(x2, y2);
  67. if (p == q) {
  68. printf("YES\n");
  69. continue;
  70. } else if (deep[p] > deep[q]) {
  71. if (LCA(p, x2) == p || LCA(p, y2) == p) printf("YES\n");
  72. else printf("NO\n");
  73. } else {
  74. if (LCA(q, x1) == q || LCA(q, y1) == q) printf("YES\n");
  75. else printf("NO\n");
  76. }
  77. }
  78. cnt = 0;
  79. memset(head, 0, sizeof(head));
  80. memset(deep, 0, sizeof(deep));
  81. memset(father, 0, sizeof(father));
  82. memset(log, 0, sizeof(log));
  83. }
  84. //fclose(stdin);
  85. //fclose(stdout);
  86. return 0;
  87. }

计数

计数.jpg

\sum max_i - min_j 可以转化为求 \sum max_i - \sum min_j

对于序列中第 i 个数字 a_i ,可以求出它在哪一段区间内的是最大值,在哪一段区间内是最小值。

设第 i 个数字左边第一个比它大的数字的下标为 L\_max[i] ,右边第一个比它大的数字的下标为 R\_max[i] ,因此就可以得出数字 a_i 有贡献的区间的个数为 (R\_max[i] - i) \times (i - L\_max[i]) ,这些区间的贡献值加起来就是 a_i \times (R\_max[i] - i) \times (i - L\_max[i]) 。同理,可以求出每个数字在哪一段区间内是最小值,用 \sum max_i - \sum min_j 就求出了最后的答案。

  1. #include <cstdio>
  2. #define MAXN 100001
  3. long long n;
  4. long long stack[MAXN], top = 0; //stack 为单调递减的栈,top 为指针下标
  5. struct node{
  6. long long pos, dis;
  7. } a[MAXN];
  8. long long L_max[MAXN], R_max[MAXN]; //存储 x 左边第一个比 x 大的以及 x 右边第一个比 x 大的
  9. long long L_min[MAXN], R_min[MAXN];
  10. void find_max() {
  11. a[0].pos = 0, a[0].dis = 2147483647;
  12. for (long long i = 1; i <= n; i++) { //维护一个单调递减的栈
  13. if (a[i].dis <= a[stack[top]].dis) { //能够添加元素
  14. top++;
  15. stack[top] = i;
  16. L_max[i] = a[stack[top - 1]].pos;
  17. } else { //为维护队列单调性,删除元素
  18. while (a[i].dis > a[stack[top]].dis) {
  19. R_max[a[stack[top]].pos] = i; //记录当前被弹出元素
  20. top--;
  21. }
  22. top++;
  23. stack[top] = i;
  24. L_max[i] = a[stack[top - 1]].pos;
  25. }
  26. }
  27. while (top >= 1) { //把栈最后几个元素赋值右边最大值
  28. R_max[a[stack[top]].pos] = n + 1;
  29. top--;
  30. }
  31. }
  32. void find_min() {
  33. a[0].pos = 0, a[0].dis = 0;
  34. for (long long i = 1; i <= n; i++) { //维护一个单调递增的栈
  35. if (a[i].dis >= a[stack[top]].dis) { //能够添加元素
  36. top++;
  37. stack[top] = i;
  38. L_min[i] = a[stack[top - 1]].pos;
  39. } else { //为维护队列单调性,删除元素
  40. while (a[i].dis < a[stack[top]].dis) {
  41. R_min[a[stack[top]].pos] = i; //记录当前被弹出元素
  42. top--;
  43. }
  44. top++;
  45. stack[top] = i;
  46. L_min[i] = a[stack[top - 1]].pos;
  47. }
  48. }
  49. while (top >= 1) { //把栈最后几个元素赋值右边最大值
  50. R_min[a[stack[top]].pos] = n + 1;
  51. top--;
  52. }
  53. }
  54. int main() {
  55. //freopen("count.in", "r", stdin);
  56. //freopen("count.out", "w", stdout);
  57. long long T;
  58. scanf("%lld", &T);
  59. while (T--) {
  60. scanf("%lld", &n);
  61. for (long long i = 1; i <= n; i++) {
  62. scanf("%lld", &a[i].dis);
  63. a[i].pos = i;
  64. }
  65. find_max(); //找到最大值贡献
  66. find_min();
  67. long long ans = 0;
  68. for (long long i = 1; i <= n; i++)
  69. ans += a[i].dis * (R_max[i] - i) * (i - L_max[i]) - a[i].dis * (R_min[i] - i) * (i - L_min[i]);
  70. printf("%lld\n", ans);
  71. top = 0;
  72. }
  73. //fclose(stdin);
  74. //fclose(stdout);
  75. return 0;
  76. }

感谢 ckw 提供正解方法~

树上统计