• 【算法随笔:HDU 3333 Turing tree】(线段树 | 离线 | 离散化 | 贪心)


     https://acm.hdu.edu.cn/showproblem.php?pid=3333icon-default.png?t=N7T8https://acm.hdu.edu.cn/showproblem.php?pid=3333https://vjudge.net.cn/problem/HDU-3333icon-default.png?t=N7T8https://vjudge.net.cn/problem/HDU-3333

    题目很简单,给出长度为N的数组,Q次询问,每次给出区间[x, y), 问该区间内不同数字的和

    思路:

    预处理, 记录每个a[i]对应的最近相等值(max { k | k < i && a[k] == a[i])

    for(int i {}; i < N; ++i) near[i] = last[a[i]], last[a[i]] = i;

    只需在更新过程中,同时维护当前时刻,value of a[i]最后出现的下标即可

    注意到这里的last[a[i]]有越界风险(max {a[i]} == INT_MAX)

    需要离散化

    枚举每个询问区间[x, y),对于a[i], 如果有near[i] < x, 意味着原数组最近一次出现value of a[i]不在该区间[x, y)内,也就意味着a[i]是区间中第一个出现的元素,可以被记录

    由于每个区间可能有重叠,这时需要采用贪心思路和区间合并的思路,对区间左端点排序,依照次序依次处理询问区间,得到答案

    具体看代码

    1. #include
    2. #include
    3. #include
    4. constexpr auto __MAX__ { static_cast<int>(1e5) };
    5. //数组长度最大值
    6. constexpr auto __QUERIES__ { static_cast<int>(1e3) };
    7. //询问最大次数
    8. std::array<int, __MAX__ + 1> sequence {};
    9. std::array<int, __QUERIES__ + 1> x {};
    10. //询问区间左端点(闭)
    11. std::array<int, __QUERIES__ + 1> y {};
    12. //询问区间右端点(开)
    13. std::array<int, __QUERIES__ + 1> results {};
    14. //询问答案
    15. int N {};
    16. //数组长度
    17. int queries {};
    18. //询问次数
    19. std::array<int, __MAX__ + 1> uniques {};
    20. //用于离散化的辅助数组
    21. std::array<int, __MAX__ + 1> last{};
    22. //记录value: sequence[i] 最后一个出现(迭代过程)的下标
    23. std::array<int, __MAX__ + 1> left{};
    24. //记录与value: sequence[i] 相等的最近一次下标
    25. std::array<int, __MAX__ << 2 | 1> segments {};
    26. //线段树
    27. std::array<int, __QUERIES__ + 1> qtable {};
    28. //询问表排序
    29. std::array<int, __MAX__ + 1> stable {};
    30. //数组表排序
    31. constexpr int left(int index) noexcept { return x << 1; }
    32. constexpr int right(int index) noexcept { return x << 1 | 1; }
    33. //向上更新线段树,维护数组和
    34. constexpr update(int index) noexcept {
    35. segments[index] = std::plus<int>(segments[left(index)], segments[right(index)]);
    36. }
    37. int query(int first, int last) noexcept {
    38. return query(first, last, 0, N, 0);
    39. }
    40. //递归询问区间数组和
    41. int query(int first, int last, int current_first, int current_last, int index) noexcept {
    42. if(first <= current_first && last >= current_last) return segments[index];
    43. int middle { (current_first + current_last)/2 };
    44. return (
    45. first < middle ? query(first, last, current_first, middle, left(index)) : 0
    46. + last >= middle ? query(first, last, middle, current_last, right(index)) : 0 );
    47. }
    48. void change(int position, int value) noexcept {
    49. change(position, value, 0, N, 0);
    50. }
    51. //单点修改(加上固定常数),并且返回时维护线段树
    52. void change(int position, int value, int first, int last, int index) noexcept {
    53. if(first + 1 == last) return (void) (segments[index] += v);
    54. int middle { (first + last)/2 };
    55. if(position < middle) change(position, value, first, middle, left(index));
    56. else change(position, value, middle, last, right(index));
    57. update(index);
    58. }
    59. int main(void) {
    60. //读数组
    61. std::scanf("%d", &N);
    62. for(int i {}; i < N; (void) (uniques[i] = sequence[i ++]))
    63. std::scanf("%d", &sequence[i]);
    64. //读询问
    65. std::scanf("%d", &queries);
    66. for(int i {}; i < queries; ++i)
    67. std::scanf("%d%d", &x[i], &y[i]);
    68. //离散化
    69. std::sort(uniques.begin(), uniques.begin() + N);
    70. auto counter { static_cast<int>(
    71. std::distance(uniques.begin(),
    72. std::unique(uniques.begin(), uniques.begin() + N))) };
    73. for(int i {}; i < N; ++i)
    74. sequence[i] = static_cast<int>(
    75. std::distance(uniques.begin(),
    76. std::lower_bound(uniques.begin(), uniques.begin() + counter, sequence[i])));
    77. //初始化last数组
    78. std::fill_n(last.begin(), counter, -1);
    79. //更新left数组
    80. for(int i {}; i < N; ++i) {
    81. left[i] = last[sequence[i]];
    82. last[sequence[i]] = i;
    83. }
    84. /*
    85. ** 对询问和数组进行表排序,保证:
    86. ** 1: 询问区间左端点有序
    87. ** 2: 迭代过程中,最早出现的元素最先被遍历
    88. */
    89. for(int i {}; i < queries; ++i) qtable[i] = i;
    90. for(int i {}; i < N; ++i) stable[i] = i;
    91. std::sort(qtable.begin(), qtable.begin() + queries, [&](int i, int j) { return x[table[i]] < x[table[j]]; });
    92. std::sort(stable.begin(), stable.begin() + N, [&](int i, int j) { return left[stable[i]] < left[stable[j]]; });
    93. //离线处理询问
    94. int sentinel {};
    95. //遍历每个询问
    96. for(int i {}; i < queries; ++i) {
    97. //根据left数组,把在区间中的仅一次出现的元素,插入线段树
    98. while(sentinel < N && left[stable[sentinel]] < x[qtable[i]]) {
    99. change(stable[sentinel], uniques[sequence[stable[sentinel]]]);
    100. ++ sentinel;
    101. }
    102. //更新答案
    103. results[qtable[i]] = query(x[qtable[i], y[qtable[j]]);
    104. }
    105. //输出
    106. for(int i{}; i < queries; ++i) std::printf("%d%c", results[i], " \n"[! (i == queries)]);
    107. return 0;
    108. }

  • 相关阅读:
    MySQL事务隔离级别下的一些测试
    Go 语言内置类型全解析:从布尔到字符串的全维度探究
    C语言获取win11新版终端WindowsTerminal窗口句柄
    网易易盾中文点选验证码识别方法
    前端之使用webpack打包TS
    英语——语法——从句——句型和句子成分——笔记
    Linux crontab 命令定时任务设置
    SpringCloud Alibaba
    spring源码下载安装,导入idea以及编译报错问题详细解决过程
    Vue简介,模板语法,mvvm模型
  • 原文地址:https://blog.csdn.net/RFoer/article/details/136482191