题目:

洛谷 3242

分析:

明确题意:在一棵树上给定若干权值为 \(w\) 的路径 \((u,v)\) (盘子),每次给定 \((a,b)\) (水果),询问所有满足 \((u,v)\) 被 \((a,b)\) 完全覆盖的路径中第 \(k\) 小的路径的权值。

先考虑如何快速判断 \((u,v)\) 是不是 \((a,b)\) 的子路径。第一反应是充要条件是 \(a\) 和 \(b\) 分别在 \(u\) 和 \(v\) 的子树中(设 \(\mathrm{dfn}_p\) 是 \(p\) 在 dfs 序中的位置,以下默认 \(\mathrm{dfn}_u<\mathrm{dfn}_v\) 且 \(\mathrm{dfn}_a<\mathrm{dfn}_b\) )。这对于大部分情况是成立的,但是当 \(u\) 是 \(v\) 的祖先时并不成立,因为 \(v\) 的子树被包含在 \(u\) 的子树中,所以而在 \(v\) 的子树中任选两点都满足条件,但之间的路径并不能覆盖 \((u,v)\) 。画图可知,此时 \(b\) 仍要求在 \(v\) 的子树中,而 \(a\) 应当在 \(p\) 的子树外(注意不是 \(u\) 的子树),其中 \(p\) 是从 \(u\) 到 \(v\) 的路径上除 \(u\) 之外的第一个点。详见下图(博客特色画风):

总结一下,如果 \((u,v)\) 被 \((a,b)\) 完全覆盖,那么需要满足:

如果 \((u,v)\) 不是一条深度递增的链,那么需要满足( \(\mathrm{out}_p\) 表示 \(p\) 的子树中最大的 \(\mathrm{dfn}\) ):

\[\mathrm{dfn}_a\in [\mathrm{dfn}_u,\mathrm{out}_u]\ 且\ \mathrm{dfn}_b\in [\mathrm{dfn}_v,\mathrm{out}_v]
\]

否则需要满足( \(p\) 的含义见上):

\[\mathrm{dfn}_a\in [1,\mathrm{dfn}_p)\cup (\mathrm{out}_p,n]\ 且\ \mathrm{dfn}_b\in [\mathrm{dfn}_v,\mathrm{out}_v]
\]

对于求第 \(k\) 小的题,一个很常见的策略是二分。二分一个答案 \(x\) ,把所有权值小于等于 \(x\) 的点都插入数据结构,然后查询符合要求的点的数量,若大于等于 \(k\) 则向下二分,否则向上二分。

对于这道题,由于限制是二维的,所以需要树套树。插入一个路径 \((u,v)\) 相当于给它能贡献到的地方的答案全部加 1 。对于 \(u\) 不是 \(v\) 的祖先的情况,这个「地方」是一维范围为 \([\mathrm{dfn}_u,\mathrm{out}_u]\) ,另一维范围为 \([\mathrm{dfn}_v,\mathrm{out}_v]\) 的矩形;否则,贡献到的是两个矩形(自行根据上面的式子构造)。查询一个路径 \((a,b)\) 相当于查询点 \((\mathrm{dfn}_a,\mathrm{dfn}_b)\) 的权值。由于是多组询问,所以套上整体二分,时间复杂度 \(O(n\log^3 n)\) 。

然而实测树套树会不幸被 卡常 卡复杂度。可以用类似离线扫描线的思想,把修改和询问按第一维的 dfn 排序,然后按 dfn 从小到大处理,并把修改拆成两次: 在 \(\mathrm{dfn}_u\) 处给 \([\mathrm{dfn}_v,\mathrm{out}_v]\) 加 1 ,在 \(\mathrm{out}_u+1\) 处给上述区间减 1 。用树状数组维护区间修改和单点查询,时间复杂度 \(O(n\log^2 n)\) 。

代码:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cctype>
  4. #include <algorithm>
  5. using namespace std;
  6. namespace zyt
  7. {
  8. template<typename T>
  9. inline bool read(T &x)
  10. {
  11. char c;
  12. bool f = false;
  13. x = 0;
  14. do
  15. c = getchar();
  16. while (c != EOF && c != '-' && !isdigit(c));
  17. if (c == EOF)
  18. return true;
  19. if (c == '-')
  20. f = true, c = getchar();
  21. do
  22. x = x * 10 + c - '0', c = getchar();
  23. while (isdigit(c));
  24. if (f)
  25. x = -x;
  26. return true;
  27. }
  28. template<typename T>
  29. inline void write(T x)
  30. {
  31. static char buf[20];
  32. char *pos = buf;
  33. if (x < 0)
  34. putchar('-'), x = -x;
  35. do
  36. *pos++ = x % 10 + '0';
  37. while (x /= 10);
  38. while (pos > buf)
  39. putchar(*--pos);
  40. }
  41. const int N = 4e4 + 10, B = 16;
  42. int n, head[N], ecnt, fa[N][B], dep[N], dfn[N], dfncnt, out[N], id[N], ans[N];
  43. struct edge
  44. {
  45. int to, next;
  46. }e[N << 1];
  47. struct _plt
  48. {
  49. int u, v, c, nxt;
  50. bool operator < (const _plt &b) const
  51. {
  52. return c < b.c;
  53. }
  54. }plt[N]; // plate
  55. int idplt[N];
  56. struct _frt
  57. {
  58. int u, v, k, id;
  59. }frt[N]; // fruit
  60. bool cmpdfnu(const int a, const int b)
  61. {
  62. return dfn[frt[a].u] < dfn[frt[b].u];
  63. }
  64. namespace Tree_Array
  65. {
  66. int data[N];
  67. inline int lowbit(const int x)
  68. {
  69. return x & (-x);
  70. }
  71. void add(int a, const int x)
  72. {
  73. while (a <= n)
  74. data[a] += x, a += lowbit(a);
  75. }
  76. int query(int a)
  77. {
  78. int ans = 0;
  79. while (a)
  80. ans += data[a], a -= lowbit(a);
  81. return ans;
  82. }
  83. void add(const int l, const int r, const int x)
  84. {
  85. add(l, x), add(r + 1, -x);
  86. }
  87. }
  88. void add(const int a, const int b)
  89. {
  90. e[ecnt] = (edge){b, head[a]}, head[a] = ecnt++;
  91. }
  92. void dfs(const int u, const int f)
  93. {
  94. fa[u][0] = f;
  95. for (int i = 1; i < B; i++)
  96. fa[u][i] = fa[fa[u][i - 1]][i - 1];
  97. dep[u] = dep[f] + 1;
  98. dfn[u] = ++dfncnt;
  99. for (int i = head[u]; ~i; i = e[i].next)
  100. {
  101. int v = e[i].to;
  102. if (v == f)
  103. continue;
  104. dfs(v, u);
  105. }
  106. out[u] = dfncnt;
  107. }
  108. inline int lca(int a, int b)
  109. {
  110. if (dep[a] < dep[b])
  111. swap(a, b);
  112. for (int i = B - 1; i >= 0; i--)
  113. if (dep[fa[a][i]] >= dep[b])
  114. a = fa[a][i];
  115. if (a == b)
  116. return a;
  117. for (int i = B - 1; i >= 0; i--)
  118. if (fa[a][i] != fa[b][i])
  119. a = fa[a][i], b = fa[b][i];
  120. return fa[a][0];
  121. }
  122. namespace Divide_Together
  123. {
  124. struct _mdf
  125. {
  126. int pos, l, r, x;
  127. _mdf() {}
  128. _mdf(const int _pos, const int _l, const int _r, const int _x)
  129. : pos(_pos), l(_l), r(_r), x(_x) {}
  130. bool operator < (const _mdf &b) const
  131. {
  132. return pos < b.pos;
  133. }
  134. }mdf[N << 1];
  135. int cnt;
  136. void insert(const int l, const int r, const int ll, const int rr, const int x)
  137. {
  138. mdf[cnt++] = _mdf(l, ll, rr, x);
  139. mdf[cnt++] = _mdf(r + 1, ll, rr, -x);
  140. }
  141. void insert(const int i, const int x)
  142. {
  143. if (plt[i].nxt)
  144. {
  145. insert(1, dfn[plt[i].nxt] - 1, dfn[plt[i].v], out[plt[i].v], x);
  146. if (out[plt[i].nxt] < n)
  147. insert(dfn[plt[i].v], out[plt[i].v], out[plt[i].nxt] + 1, n, x);
  148. }
  149. else
  150. insert(dfn[plt[i].u], out[plt[i].u], dfn[plt[i].v], out[plt[i].v], x);
  151. }
  152. void solve(const int l, const int r, const int ql, const int qr)
  153. {
  154. using Tree_Array::query;
  155. using Tree_Array::add;
  156. if (l == r)
  157. {
  158. for (int i = ql; i <= qr; i++)
  159. ans[frt[id[i]].id] = plt[l].c;
  160. return;
  161. }
  162. static int buf1[N], buf2[N];
  163. int cnt1 = 0, cnt2 = 0;
  164. int mid = (l + r) >> 1;
  165. cnt = 0;
  166. for (int i = l; i <= mid; i++)
  167. insert(i, 1);
  168. sort(mdf, mdf + cnt);
  169. int now = 0;
  170. for (int i = ql; i <= qr; i++)
  171. {
  172. while (now < cnt && mdf[now].pos <= dfn[frt[id[i]].u])
  173. add(mdf[now].l, mdf[now].r, mdf[now].x), ++now;
  174. int tmp = query(dfn[frt[id[i]].v]);
  175. if (tmp >= frt[id[i]].k)
  176. buf1[cnt1++] = id[i];
  177. else
  178. buf2[cnt2++] = id[i], frt[id[i]].k -= tmp;
  179. }
  180. for (int i = 0; i < cnt1; i++)
  181. id[i + ql] = buf1[i];
  182. for (int i = 0; i < cnt2; i++)
  183. id[i + cnt1 + ql] = buf2[i];
  184. for (int i = 0; i < now; i++)
  185. add(mdf[i].l, mdf[i].r, -mdf[i].x);
  186. solve(l, mid, ql, ql + cnt1 - 1);
  187. solve(mid + 1, r, ql + cnt1, qr);
  188. }
  189. }
  190. int work()
  191. {
  192. int p, q;
  193. read(n), read(p), read(q);
  194. memset(head, -1, sizeof(int[n + 1]));
  195. for (int i = 1; i < n; i++)
  196. {
  197. int a, b;
  198. read(a), read(b);
  199. add(a, b), add(b, a);
  200. }
  201. dfs(1, 0);
  202. for (int i = 0; i < p; i++)
  203. {
  204. read(plt[i].u), read(plt[i].v), read(plt[i].c);
  205. if (dfn[plt[i].u] > dfn[plt[i].v])
  206. swap(plt[i].u, plt[i].v);
  207. plt[i].nxt = 0;
  208. if (lca(plt[i].u, plt[i].v) == plt[i].u)
  209. {
  210. int &tmp = plt[i].nxt;
  211. tmp = plt[i].v;
  212. for (int j = B - 1; j >= 0; j--)
  213. if (dep[fa[tmp][j]] > dep[plt[i].u])
  214. tmp = fa[tmp][j];
  215. }
  216. }
  217. sort(plt, plt + p);
  218. for (int i = 0; i < q; i++)
  219. {
  220. id[i] = i;
  221. read(frt[i].u), read(frt[i].v), read(frt[i].k);
  222. if (dfn[frt[i].u] > dfn[frt[i].v])
  223. swap(frt[i].u, frt[i].v);
  224. frt[i].id = i;
  225. }
  226. sort(id, id + q, cmpdfnu);
  227. Divide_Together::solve(0, p - 1, 0, q - 1);
  228. for (int i = 0; i < q; i++)
  229. write(ans[i]), putchar('\n');
  230. return 0;
  231. }
  232. }
  233. int main()
  234. {
  235. return zyt::work();
  236. }

【BZOJ4009_洛谷3242】[HNOI2015] 接水果(整体二分)的更多相关文章

  1. [洛谷P3242] [HNOI2015]接水果

    洛谷题目链接:[HNOI2015]接水果 题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, 她觉得这个游戏太简 ...

  2. 洛谷 P3242 [HNOI2015]接水果 解题报告

    P3242 [HNOI2015]接水果 题目描述 风见幽香非常喜欢玩一个叫做 \(osu!\) 的游戏,其中她最喜欢玩的模式就是接水果.由于她已经\(DT\) \(FC\) 了\(\tt{The\ b ...

  3. ●洛谷P3242 [HNOI2015]接水果

    题链: https://www.luogu.org/problemnew/show/P3242 题解: 整体二分,扫描线+树状数组. 详细的题解:http://blog.csdn.net/thy_as ...

  4. BZOJ.4009.[HNOI2015]接水果(整体二分 扫描线)

    LOJ BZOJ 洛谷 又是一个三OJ rank1!=w= \(Description\) (还是感觉,为啥非要出那种题目背景啊=-=直接说不好么) 给定一棵树和一个路径集合(每条路径有一个权值).\ ...

  5. [BZOJ4009][HNOI2015]接水果(整体二分)

    [HNOI2015]接水果 时间限制:60s      空间限制:512MB 题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果. 由于她已经DT FC 了The b ...

  6. [bzoj4009] [HNOI2015]接水果 整体二分+扫描线+dfs序+树状数组

    Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果. 由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更 加 ...

  7. [HNOI2015]接水果[整体二分]

    [HNOI2015]接水果 给出一个树上路径集合\(S\) 多次询问\(x,y\)中的\(k\)小值 如果你问我数列上那么我会 树上的话 树上差分了吧直接?- 令 \(st_x<st_y\) 1 ...

  8. BZOJ4009:[HNOI2015]接水果(整体二分版)

    浅谈离线分治算法:https://www.cnblogs.com/AKMer/p/10415556.html 题目传送门:https://lydsy.com/JudgeOnline/problem.p ...

  9. 洛谷P3527 [POI2011]MET-Meteors(整体二分)

    传送门 整体二分 先二分一个答案,判断是否可行,把可行的全都扔到左边,不可行的扔到右边 判断是否可行用树状数组就行 具体细节看代码好了 整体二分细节真多……也可能是我大脑已经退化了? //minamo ...

随机推荐

  1. noip模拟赛 道路分组

    分析:因为每一组编号都是连续的嘛,所以能分成一组的尽量分,每次加边后dfs判断一下1和n是否连通.有向图的判连通没有什么很快的方法,特别注意,并查集是错的!这个算法可以得到60分. 事实上每一次都不需 ...

  2. Spring Data Jpa系列教程--------实体解析和关联关系

    Spring Data Jpa是基于HIbernate开发的,所以建立实体建的实体和映射关系需要好好好的去了解一下,本文有以下内容,实体管理器介绍,实体与数据库表的映射介绍,关联关系(一对多,多对多) ...

  3. Qmake 工具编译调试

    Qmake 工具编译调试 2015年4月9日星期四 18:38:06 1. 确定qmaek 路径 [root@roger ~]# which qmake /usr/lib/qt-3.3/bin/qma ...

  4. java读取大文本文件

    原文:http://blog.csdn.net/k21325/article/details/53886160 小文件当然可以直接读取所有,然后放到内存中,但是当文件很大的时候,这个方法就行不通了,内 ...

  5. 1072. Gas Station (30)【最短路dijkstra】——PAT (Advanced Level) Practise

    题目信息 1072. Gas Station (30) 时间限制200 ms 内存限制65536 kB 代码长度限制16000 B A gas station has to be built at s ...

  6. 改动Centosserver主机名称

    1.暂时改动server主机名称: hostname myhost. myhost为你指定的主机名称. 2.永久性的改动主机名称 Centosserver安装好之后.默认的主机名为:localhost ...

  7. JSP简单练习-包装类综合应用实例

    <%@ page contentType="text/html; charset=gb2312" %><!-- JSP指令标签 --> <%@ pag ...

  8. Linux地址ping不通情况怎么办?

    查看原文:http://www.ibloger.net/article/325.html Linux地址ping不通情况怎么办? 问题:今天写了一个微信支付的项目.有一个class中使用了httpPo ...

  9. web 开发之js---js 调用视频播放

    var popWindow;var videoWindow;var videoWindowF;var currentVideo=null;var currentVideoTitle="&qu ...

  10. YTU 2642: 填空题:类模板---求数组的最大值

    2642: 填空题:类模板---求数组的最大值 时间限制: 1 Sec  内存限制: 128 MB 提交: 646  解决: 446 题目描述   类模板---求数组的最大值    找出一个数组中的元 ...