题面

题解

树链剖分 + 主席树

先考虑一个简单一点的问题:

【LNOI2014】LCA

我们考察\(dep[\mathrm{LCA}(i, x)]\)的性质,发现它是\(i\)和\(x\)的链交的长度。

那么对每个\(i\)所在的链打一个区间加标记,询问时算一下\(x\)所在的链的区间和即可。

如果有\(l \leq i \leq r\)的限制,就进行离线处理即可。

接下来再考虑一个问题:

给定一棵\(n\)个节点的树,边有边权。

多次询问,每次给定\(x\),求\(\sum_{i = 1}^n dis(i, x)\)。

因为\(dis(i, x) = dis[i] + dis[x] - 2 \times dis[\mathrm{LCA}(i, x)]\),其中\(dis[i]\)表示\(i\)到根节点的距离,于是又变成了前面那一个问题。

但是这一题有年龄限制,所以用可持久化线段树维护一下即可。

这一题的线段树要标记永久化,不然空间会炸。​

时间复杂度\(\mathrm{O}(n\log_2^2 n)\)

动态点分治

动态点分治也可以维护距离和,就是维护两个数组:一个维护子树中所有点到这个点的距离和以及子树中的所有点到这个点到这个点的父亲节点的距离和就可以了,计算答案的时候两个相减即可。

然后发现这道题目不太一样,带了年龄,于是不能用数组。

对于每个点开一个vector,记录上面的信息,同时记录年龄,然后将每个vector按照年龄从小到大排序,并求出前缀和,然后二分查找即可。

时间复杂度\(\mathrm{O}(n\log_2^2 n)\)

代码

树链剖分 + 主席树

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<cctype>
  4. #include<algorithm>
  5. #define RG register
  6. template<typename T>
  7. inline T read()
  8. {
  9. T data = 0, w = 1; char ch = getchar();
  10. while(ch != '-' && (!isdigit(ch))) ch = getchar();
  11. if(ch == '-') w = -1, ch = getchar();
  12. while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
  13. return data * w;
  14. }
  15. const int maxn(200000);
  16. struct edge { int next, to, dis; } e[maxn << 1];
  17. int head[maxn], e_num, n, Q, A, age[maxn];
  18. inline void add_edge(int from, int to, int dis)
  19. {
  20. e[++e_num] = (edge) {head[from], to, dis};
  21. head[from] = e_num;
  22. }
  23. int size[maxn], fa[maxn], heavy[maxn], dis[maxn];
  24. void dfs(int x)
  25. {
  26. size[x] = 1;
  27. for(RG int i = head[x]; i; i = e[i].next)
  28. {
  29. int to = e[i].to; if(to == fa[x]) continue;
  30. fa[to] = x, dis[to] = dis[x] + e[i].dis;
  31. dfs(to); size[x] += size[to];
  32. if(size[heavy[x]] < size[to]) heavy[x] = to;
  33. }
  34. }
  35. int pos[maxn], belong[maxn], cnt_pos[maxn], cnt;
  36. void dfs(int x, int chain)
  37. {
  38. cnt_pos[pos[x] = ++cnt] = x;
  39. belong[x] = chain;
  40. if(!heavy[x]) return;
  41. dfs(heavy[x], chain);
  42. for(RG int i = head[x]; i; i = e[i].next)
  43. {
  44. int to = e[i].to;
  45. if(to == fa[x] || to == heavy[x]) continue;
  46. dfs(to, to);
  47. }
  48. }
  49. inline int cmp(int x, int y) { return age[x] < age[y]; }
  50. int son[2][maxn << 6], cur;
  51. long long sum[maxn << 6], lazy[maxn << 6];
  52. void build(int &x, int l = 1, int r = n)
  53. {
  54. x = ++cur; if(l == r) return;
  55. int mid = (l + r) >> 1;
  56. build(son[0][x], l, mid);
  57. build(son[1][x], mid + 1, r);
  58. }
  59. void update(int &x, int ql, int qr, int l = 1, int r = n)
  60. {
  61. ++cur; son[0][cur] = son[0][x], son[1][cur] = son[1][x];
  62. sum[cur] = sum[x], lazy[cur] = lazy[x]; x = cur;
  63. sum[x] += dis[cnt_pos[qr]] - dis[fa[cnt_pos[ql]]];
  64. if(ql == l && r == qr) return (void) (++lazy[x]);
  65. int mid = (l + r) >> 1;
  66. if(ql <= mid) update(son[0][x], ql, std::min(qr, mid), l, mid);
  67. if(mid < qr) update(son[1][x], std::max(mid + 1, ql), qr, mid + 1, r);
  68. }
  69. long long query(int x, int ql, int qr, long long ex = 0, int l = 1, int r = n)
  70. {
  71. if(ql == l && r == qr) return sum[x] + 1ll * ex *
  72. (dis[cnt_pos[qr]] - dis[fa[cnt_pos[ql]]]);
  73. int mid = (l + r) >> 1; long long ans = 0; ex += lazy[x];
  74. if(ql <= mid) ans = query(son[0][x], ql, std::min(qr, mid), ex, l, mid);
  75. if(mid < qr) ans += query(son[1][x], std::max(mid + 1, ql), qr, ex, mid + 1, r);
  76. return ans;
  77. }
  78. long long sdis[maxn];
  79. int root[maxn], id[maxn], S[maxn];
  80. long long calc(int x, int k)
  81. {
  82. long long ans = 0;
  83. while(belong[x] != 1) ans += query(root[k], pos[belong[x]], pos[x]),
  84. x = fa[belong[x]];
  85. return ans + query(root[k], 1, pos[x]);
  86. }
  87. int main()
  88. {
  89. n = read<int>(), Q = read<int>(), A = read<int>();
  90. for(RG int i = 1; i <= n; i++) id[i] = i, S[i] = age[i] = read<int>();
  91. std::sort(S + 1, S + n + 1), std::sort(id + 1, id + n + 1, cmp);
  92. for(RG int i = 1, a, b, c; i < n; i++)
  93. a = read<int>(), b = read<int>(), c = read<int>(),
  94. add_edge(a, b, c), add_edge(b, a, c);
  95. dfs(1); dfs(1, 1); build(root[0]);
  96. for(RG int i = 1; i <= n; i++)
  97. {
  98. int x = id[i]; sdis[i] = sdis[i - 1] + dis[x];
  99. root[i] = root[i - 1];
  100. while(belong[x] != 1) update(root[i], pos[belong[x]], pos[x]),
  101. x = fa[belong[x]];
  102. update(root[i], 1, pos[x]);
  103. }
  104. for(long long ans = 0; Q--;)
  105. {
  106. int x = read<int>();
  107. long long a = read<long long>(), b = read<long long>();
  108. int L = std::min((ans + a) % A, (ans + b) % A);
  109. int R = std::max((ans + a) % A, (ans + b) % A);
  110. L = std::lower_bound(S + 1, S + n + 1, L) - S;
  111. R = std::lower_bound(S + 1, S + n + 1, R + 1) - S - 1;
  112. ans = 1ll * (R - L + 1) * dis[x] + sdis[R] - sdis[L - 1]
  113. - 2ll * (calc(x, R) - calc(x, L - 1));
  114. printf("%lld\n", ans);
  115. }
  116. return 0;
  117. }

动态点分治

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<cctype>
  4. #include<algorithm>
  5. #include<vector>
  6. #define RG register
  7. inline int read()
  8. {
  9. int data = 0, w = 1; char ch = getchar();
  10. while(ch != '-' && (!isdigit(ch))) ch = getchar();
  11. if(ch == '-') w = -1, ch = getchar();
  12. while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
  13. return data * w;
  14. }
  15. const int maxn(150010), LogN(20);
  16. struct node { int val; long long dis, sum; };
  17. inline bool operator < (const node &lhs, const node &rhs)
  18. {
  19. return lhs.val == rhs.val ? lhs.dis == rhs.dis ?
  20. lhs.sum < rhs.sum : lhs.dis < rhs.dis : lhs.val < rhs.val;
  21. }
  22. std::vector<node> va[maxn], vb[maxn];
  23. struct edge { int next, to, dis; } e[maxn << 1];
  24. int head[maxn], e_num, age[maxn], n, Q, A, Log[maxn << 1], fa[maxn];
  25. int pos[maxn], cnt, size[maxn], _max, SIZE, root, vis[maxn];
  26. long long dep[maxn], ST[LogN][maxn << 1];
  27. inline void add_edge(int from, int to, int dis)
  28. {
  29. e[++e_num] = (edge) {head[from], to, dis};
  30. head[from] = e_num;
  31. }
  32. void dfs(int x, int fa)
  33. {
  34. ST[0][pos[x] = ++cnt] = dep[x];
  35. for(RG int i = head[x]; i; i = e[i].next)
  36. {
  37. int to = e[i].to; if(to == fa) continue;
  38. dep[to] = dep[x] + e[i].dis; dfs(to, x);
  39. ST[0][++cnt] = dep[x];
  40. }
  41. }
  42. long long Dis(int x, int y)
  43. {
  44. int t = dep[x] + dep[y];
  45. x = pos[x], y = pos[y]; if(x > y) std::swap(x, y);
  46. int k = Log[y - x + 1];
  47. return t - 2 * std::min(ST[k][x], ST[k][y - (1 << k) + 1]);
  48. }
  49. void getRoot(int x, int fa)
  50. {
  51. size[x] = 1; int _ = 0;
  52. for(RG int i = head[x]; i; i = e[i].next)
  53. {
  54. int to = e[i].to; if(vis[to] || to == fa) continue;
  55. getRoot(to, x); size[x] += size[to]; _ = std::max(_, size[to]);
  56. }
  57. _ = std::max(_, SIZE - size[x]);
  58. if(_ < _max) root = x, _max = _;
  59. }
  60. void Solve(int x)
  61. {
  62. vis[x] = 1;
  63. for(RG int i = head[x]; i; i = e[i].next)
  64. {
  65. int to = e[i].to; if(vis[to]) continue;
  66. SIZE = _max = size[to], root = to; getRoot(to, x);
  67. fa[root] = x; Solve(root);
  68. }
  69. }
  70. long long query(int x, int a)
  71. {
  72. long long ans = 0;
  73. for(RG int i = x; i; i = fa[i])
  74. {
  75. int t = std::lower_bound(va[i].begin(), va[i].end(),
  76. (node) {a, 0, 0}) - va[i].begin() - 1;
  77. ans += va[i][t].sum + t * Dis(i, x);
  78. }
  79. for(RG int i = x; fa[i]; i = fa[i])
  80. {
  81. int t = std::lower_bound(vb[i].begin(), vb[i].end(),
  82. (node) {a, 0, 0}) - vb[i].begin() - 1;
  83. ans -= vb[i][t].sum + t * Dis(fa[i], x);
  84. }
  85. return ans;
  86. }
  87. int main()
  88. {
  89. n = read(), Q = read(), A = read();
  90. for(RG int i = 1; i <= n; i++) age[i] = read();
  91. for(RG int i = 1, a, b, c; i < n; i++)
  92. a = read(), b = read(), c = read(),
  93. add_edge(a, b, c), add_edge(b, a, c);
  94. dfs(1, 0); Log[0] = -1;
  95. for(RG int i = 1; i <= cnt; i++) Log[i] = Log[i >> 1] + 1;
  96. for(RG int i = 1; i <= Log[cnt]; i++)
  97. for(RG int j = 1; j <= cnt - (1 << i) + 1; j++)
  98. ST[i][j] = std::min(ST[i - 1][j], ST[i - 1][j + (1 << (i - 1))]);
  99. SIZE = _max = n; getRoot(1, 0); Solve(root);
  100. for(RG int i = 1; i <= n; i++) for(RG int j = i; j; j = fa[j])
  101. va[j].push_back((node) {age[i], Dis(i, j), 0}),
  102. vb[j].push_back((node) {age[i], Dis(i, fa[j]), 0});
  103. for(RG int i = 1; i <= n; i++)
  104. {
  105. va[i].push_back((node) {-1, 0, 0});
  106. vb[i].push_back((node) {-1, 0, 0});
  107. va[i].push_back((node) {1 << 30, 0, 0});
  108. vb[i].push_back((node) {1 << 30, 0, 0});
  109. std::sort(va[i].begin(), va[i].end());
  110. std::sort(vb[i].begin(), vb[i].end());
  111. for(RG int j = 1; j < (int)va[i].size(); j++)
  112. va[i][j].sum = va[i][j - 1].sum + va[i][j].dis;
  113. for(RG int j = 1; j < (int)vb[i].size(); j++)
  114. vb[i][j].sum = vb[i][j - 1].sum + vb[i][j].dis;
  115. }
  116. for(long long ans = 0; Q--;)
  117. {
  118. int x = read(), a = read(), b = read();
  119. a = (ans + a) % A, b = (ans + b) % A;
  120. if(a > b) std::swap(a, b);
  121. printf("%lld\n", ans = query(x, b + 1) - query(x, a));
  122. }
  123. return 0;
  124. }

【HNOI2015】开店的更多相关文章

  1. [HNOI2015]开店 树链剖分,主席树

    [HNOI2015]开店 LG传送门 蒟蒻表示不会动态淀粉质. 先把点按年龄排序, 设\(dis[i]\)表示\(i\)到根的距离. 把我们要算的东西稍微变下形:\(ans\) \[ = \sum \ ...

  2. 洛谷 P3241 [HNOI2015]开店 解题报告

    P3241 [HNOI2015]开店 题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱. 这样的想法当然非 ...

  3. [BZOJ4012][HNOI2015]开店(动态点分治,树链剖分)

    4012: [HNOI2015]开店 Time Limit: 70 Sec  Memory Limit: 512 MBSubmit: 2168  Solved: 947[Submit][Status] ...

  4. 【BZOJ4012】[HNOI2015]开店 动态树分治+二分

    [BZOJ4012][HNOI2015]开店 Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点 ...

  5. BZOJ4012 [HNOI2015]开店

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

  6. bzoj 4012: [HNOI2015]开店

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

  7. bzoj 4012: [HNOI2015]开店 主席树

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

  8. BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector

    题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...

  9. BZOJ4012: [HNOI2015]开店【动态点分治】

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

  10. BZOJ4012 [HNOI2015]开店 (动态点分治)

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

随机推荐

  1. 非定制UIImagePickerController的使用

    非定制UIImagePickerController的使用 效果: 源码: // // ViewController.m // ImagePic // // Created by XianMingYo ...

  2. java判断字符串内容

    java判断字符串是否全为数字 String str = "032";boolean isNum = str.matches("[0-9]+"); java判断 ...

  3. python第三方库——xlrd和xlwt操作Excel文件学习

    python第三方库——xlrd和xlwt操作Excel文件学习 1安装: C:\Users\Lenovo>pip install xlwtCollecting xlwt  Downloadin ...

  4. 第二次项目冲刺(Beta版本) 合集

    小组成员 [组长]金盛昌(201421122043).刘文钊(20142112255).陈笑林(201421122042) 张俊逸(201421122044).陈志建(201421122040).陈金 ...

  5. sql server 使用链接服务器连接Oracle,openquery查询数据

      对接问题描述:不知道正式库oracle数据库账户密码,对方愿意在对方的客户端上输入账号和密码,但不告诉我们 解决方案:使用一台sql server作为中间服务器,可以通过转存数据到sql serv ...

  6. 纯css3云彩动画效果

      效果描述: 纯CSS3实现的云彩动画飘动效果 非常逼真实用 使用方法: 1.将body中的代码部分拷贝到你的页面中 2.引入对应的CSS文件即可

  7. 9、Android---UI---Material Design

    9.1.什么是Material Design 由谷歌的设计师基于传统优秀设计原则,结合丰富的创意和科学技术所发明的一套全新的界面设计语言 包含了视觉.运行.互动等效果 Material Design的 ...

  8. virtualbox+vagrant学习-2(command cli)-6-vagrant init命令

    Init——创建Vagrantfile文件 格式: vagrant init [options] [name [url]] 通过创建初始的Vagrantfile文件(如果不存在的话),将当前目录初始化 ...

  9. HDU 1203 01背包变形题,(新思路)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1203 I NEED A OFFER! Time Limit: 2000/1000 MS (Java/ ...

  10. 【OC底层】Category、+load方法、+initialize方法原理

    Category原理 - Category编译之后的底层结构是 struct categroy_t,里面存储着分类对象方法.属性.协议信息- 当程序运行时,通过runtime动态的将分类的方法.属性. ...