直接向原树加子树是不可能的
考虑重新建立这样一颗树,我们称之为 S 树
将每次需要添加的子树看做一个点,称之为 S 点
新建的树就是由这些点构成的,
那么树的大小是合理的
初始节点为整棵原树
由于添加的子树的节点的编号一定是连续的一段区间
树上的每个节点维护 l, r, rt
分别表示 左端点, 右端点, 这棵子树的根在原树上的编号
定义
宏观树:只有 S 点构成的树 -> S 树
微观树:按照题目描述所形成的树,当然这棵树是不会建立出来的
所有的询问操作都需要微观树的信息
首先考虑对于任意合法的点 B, 如何找出它在原树上的对应点 b, 显然 b 的值域为 [1, n]
S 点满足 l, r 单调
因此首先二分出 B 点在那个 S 点中,
S 点代表的子树的编号是 [S.l, S.r]
所以 B 在该子树中的编号的大小次序为第 B - S.l + 1;
查询区间 k 大 => 主席树
这样的话只需对原树建立主席树
查询以 S.rt 为根的子树中编号第 B - S.l + 1 小的编号就是 b
Link_super 函数是核心
然后考虑查询时所要维护的东西
由于查询树上点对之间的距离,所以只需知道深度就可以解决
维护这么 3 种深度
deep[] 原树的深度
Super_dep[] S 树的深度(宏观树)
Big_deep[] 微观树的深度,当然并不去维护所有存在的点的深度
Big_deep[] 相当于是维护的宏观树,只不过所有 S 点的 Big_deep[] 都是微观树上 S 的真实深度值
这里可以这样理解:
若 Link_super(u, v), u 的值域 [1, n],
查询的 v 所在 S 树的 S 点的编号为 X;
S 的当前总数 + 1 为 Y;
这样的话 Big_deep[y] = Big_deep[x] + Dis(v, X.rt);
Dis 函数为查询原树上两点之间的距离
查询时分类讨论

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string> using namespace std; #define LL long long #define gc getchar()
inline int read() {int x = ; char c = gc; while(c < '' || c > '') c = gc;
while(c >= '' && c <= '') x = x * + c - '', c = gc; return x;}
inline LL read_LL() {LL x = ; char c = gc; while(c < '' || c > '') c = gc;
while(c >= '' && c <= '') x = x * + c - '', c = gc; return x;}
#undef gc const int N = 5e5 + ; int n, m, q;
int head[N], cnt;
LL deep[N];
int Super_node;
LL total;
struct Node {int u, v, nxt;} G[N << ]; inline void Link(int u, int v) {
G[++ cnt].v = v, G[cnt].nxt = head[u], head[u] = cnt;
} int Hjtjs; struct NOde {
int fa[N], size[N], topp[N], tree[N], lst[N], rst[N], tree_js, son[N], bef[N];
int Lson[N << ], Rson[N << ], Size[N << ], Root[N]; void Dfs_1(int u, int F_, int depth) {
deep[u] = depth, fa[u] = F_, size[u] = ;
for(int i = head[u]; ~ i; i = G[i].nxt) {
if(G[i].v == F_) continue;
Dfs_1(G[i].v, u, depth + );
size[u] += size[G[i].v];
if(size[G[i].v] > size[son[u]]) son[u] = G[i].v;
}
} void Dfs_2(int u, int tp) {
topp[u] = tp, tree[u] = ++ tree_js, lst[u] = tree[u], bef[tree_js] = u;
if(!son[u]) {rst[u] = tree[u]; return ;}
Dfs_2(son[u], tp);
for(int i = head[u]; ~ i; i = G[i].nxt) {if(G[i].v != fa[u] && G[i].v != son[u]) Dfs_2(G[i].v, G[i].v);}
rst[u] = tree_js;
} void Fill(int x, int y) {Lson[x] = Lson[y], Rson[x] = Rson[y], Size[x] = Size[y];} void Insert(int &rt, int l, int r, int x) {
Fill(++ Hjtjs, rt);
rt = Hjtjs;
Size[rt] ++;
if(l == r) return ;
int mid = (l + r) >> ;
if(x <= mid) Insert(Lson[rt], l, mid, x);
else Insert(Rson[rt], mid + , r, x);
} void Build_tree() {
Dfs_1(, , );
Dfs_2(, );
for(int i = ; i <= n; i ++) {Root[i] = Root[i - ]; Insert(Root[i], , n, bef[i]);}
} inline int Lca(int x, int y) {
int tpx = topp[x], tpy = topp[y];
while(tpx != tpy) {
if(deep[tpx] < deep[tpy]) swap(x, y), swap(tpx, tpy);
x = fa[tpx], tpx = topp[x];
}
return deep[x] < deep[y] ? x : y;
} inline int Dis(int x, int y) {int lca = Lca(x, y); return deep[x] + deep[y] - * deep[lca];} int Sec_A(int ljd, int rjd, int l, int r, int k) {
if(l == r) return l;
int mid = (l + r) >> ;
int imp = Size[Lson[rjd]] - Size[Lson[ljd]];
if(k <= (Size[Lson[rjd]] - Size[Lson[ljd]])) return Sec_A(Lson[ljd], Lson[rjd], l, mid, k);
else return Sec_A(Rson[ljd], Rson[rjd], mid + , r, k - (Size[Lson[rjd]] - Size[Lson[ljd]]));
} } Tree; struct Node_ {
#define E exit(0) struct Node_3 {LL l, r; int rt;} Super_graph[N]; struct Pair {
// yvan shu zhong de bian hao, fei shu shang
int Bef_number;
// chao ji shu de dian de gen , shi yvan shu shang de dian de bian hao
int Super_rt;
// chao ji shu de dian de bian hao
int Super_number;
}; int Super_dep[N];
LL Big_deep[N];
int f[N][];
int fa[N]; Pair Get_information(LL x) {
int l = , r = Super_node, ans;
while(l <= r) {
int mid = (l + r) >> ;
if(x <= Super_graph[mid].r) ans = mid, r = mid - ;
else l = mid + ;
}
int nottree = Super_graph[ans].rt;
int imp = Tree.Sec_A(Tree.Root[Tree.lst[nottree] - ], Tree.Root[Tree.rst[nottree]], , n, x - Super_graph[ans].l + );
return (Pair) {imp, nottree, ans};
} void Link_Super(LL u, LL v) {
Super_node ++;
Pair Nodev = Get_information(v);
Super_graph[Super_node].l = total + , Super_graph[Super_node].r = total + Tree.size[u], Super_graph[Super_node].rt = u;
total += Tree.size[u];
Super_dep[Super_node] = Super_dep[Nodev.Super_number] + ;
int a = Nodev.Super_rt, b = Nodev.Bef_number;
Big_deep[Super_node] = Big_deep[Nodev.Super_number] + Tree.Dis(b, a) + ;
f[Super_node][] = Nodev.Super_number;
fa[Super_node] = Nodev.Bef_number;
for(int i = ; i <= ; i ++) f[Super_node][i] = f[f[Super_node][i - ]][i - ];
} int Lca(int x, int y) {
if(Super_dep[x] < Super_dep[y]) swap(x, y);
int del = Super_dep[x] - Super_dep[y];
for(int i = ; i <= ; i ++) if(( << i) & del) x = f[x][i];
if(x == y) return x;
for(int i = ; i >= ; i --) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][];
} LL Ask(LL x, LL y) {
Pair Nodex = Get_information(x), Nodey = Get_information(y);
if(Super_dep[Nodex.Super_number] <= Super_dep[Nodey.Super_number]) swap(Nodex, Nodey);
if(Nodex.Super_number == Nodey.Super_number) return Tree.Dis(Nodex.Bef_number, Nodey.Bef_number);
else {
int lca = Lca(Nodex.Super_number, Nodey.Super_number);
LL ret;
if(lca == Nodey.Super_number) {
int g = Nodex.Super_number;
for(int i = ; i >= ; i --) if(f[g][i] && Super_dep[f[g][i]] > Super_dep[lca]) g = f[g][i];
ret = Big_deep[Nodex.Super_number] - Big_deep[g] + + Tree.Dis(Nodex.Super_rt, Nodex.Bef_number);
ret += Tree.Dis(fa[g], Nodey.Bef_number);
} else {
int a = Nodex.Super_number, b = Nodey.Super_number;
for(int i = ; i >= ; i --) {if(f[a][i] && Super_dep[f[a][i]] > Super_dep[lca]) a = f[a][i];}
for(int i = ; i >= ; i --) {if(f[b][i] && Super_dep[f[b][i]] > Super_dep[lca]) b = f[b][i];}
ret = Big_deep[Nodex.Super_number] - Big_deep[a] + + Big_deep[Nodey.Super_number] - Big_deep[b] + + Tree.Dis(fa[a], fa[b]);
ret += Tree.Dis(Nodex.Super_rt, Nodex.Bef_number) + Tree.Dis(Nodey.Super_rt, Nodey.Bef_number);
}
return ret;
}
}
#undef E
} S_graph; int main() {
n = read(), m = read(), q = read();
for(int i = ; i <= n; i ++) head[i] = -;
for(int i = ; i < n; i ++) {int u = read(), v = read(); Link(u, v), Link(v, u);}
Tree.Build_tree();
total = n, Super_node = ;
S_graph.Super_dep[] = ;
S_graph.f[][] = ;
S_graph.Super_graph[].l = , S_graph.Super_graph[].r = n, S_graph.Super_graph[].rt = ;
for(int i = ; i <= m; i ++) {
LL x = read_LL(), y = read_LL();
S_graph.Link_Super(x, y);
}
for(int i = ; i <= q; i ++) {
LL x = read_LL(), y = read_LL();
cout << S_graph.Ask(x, y) << "\n";
} return ;
}

luogu 3248的更多相关文章

  1. Luogu 魔法学院杯-第二弹(萌新的第一法blog)

    虽然有点久远  还是放一下吧. 传送门:https://www.luogu.org/contest/show?tid=754 第一题  沉迷游戏,伤感情 #include <queue> ...

  2. luogu p1268 树的重量——构造,真正考验编程能力

    题目链接:http://www.luogu.org/problem/show?pid=1268#sub -------- 这道题费了我不少心思= =其实思路和标称毫无差别,但是由于不习惯ACM风格的题 ...

  3. [luogu P2170] 选学霸(并查集+dp)

    题目传送门:https://www.luogu.org/problem/show?pid=2170 题目描述 老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一 ...

  4. [luogu P2647] 最大收益(贪心+dp)

    题目传送门:https://www.luogu.org/problem/show?pid=2647 题目描述 现在你面前有n个物品,编号分别为1,2,3,--,n.你可以在这当中任意选择任意多个物品. ...

  5. Luogu 考前模拟Round. 1

    A.情书 题目:http://www.luogu.org/problem/show?pid=2264 赛中:sb题,直接暴力匹配就行了,注意一下读入和最后一句话的分句 赛后:卧槽 怎么只有40 B.小 ...

  6. luogu P2580 于是他错误的点名开始了

    luogu  P2580 于是他错误的点名开始了 https://www.luogu.org/problem/show?pid=2580 题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 他会一边 ...

  7. CJOJ 1331 【HNOI2011】数学作业 / Luogu 3216 【HNOI2011】数学作业 / HYSBZ 2326 数学作业(递推,矩阵)

    CJOJ 1331 [HNOI2011]数学作业 / Luogu 3216 [HNOI2011]数学作业 / HYSBZ 2326 数学作业(递推,矩阵) Description 小 C 数学成绩优异 ...

  8. Luogu 1349 广义斐波那契数列(递推,矩阵,快速幂)

    Luogu 1349 广义斐波那契数列(递推,矩阵,快速幂) Description 广义的斐波那契数列是指形如\[A_n=p*a_{n-1}+q*a_{n-2}\]的数列.今给定数列的两系数p和q, ...

  9. Luogu 1962 斐波那契数列(矩阵,递推)

    Luogu 1962 斐波那契数列(矩阵,递推) Description 大家都知道,斐波那契数列是满足如下性质的一个数列: f(1) = 1 f(2) = 1 f(n) = f(n-1) + f(n ...

随机推荐

  1. Ubuntu中更改默认的root用户密码,以及怎样修改用户密码

    新安装的Ubuntu系统中默认的root用户密码是多少?该怎么修改? 如题,相信许多刚接触Ubuntu系统的新手大多会遇到这个问题,那么我们该如何解决这个问题呢?Ubuntu在安装过程中并没有让我们设 ...

  2. 小白入门.有函数F(x)=(x+1)^2和G(x)=2x+1.输入x值计算F(G(x))。

    #include<stdio.h> #include<math.h> void main() { float x,y; printf("Please input x\ ...

  3. 简单即时通讯、聊天室--java NIO版本

    实现的功能: 运行一个服务端,运行多个客户端.在客户端1,发送消息,其余客户端都能收到客户端1发送的消息. 重点: 1.ByteBuffer在使用时,注意flip()方法的调用,否则读取不到消息. 服 ...

  4. WxWidgets与其他工具包的比较(15种方案)

    一些一般注意事项: wxWidgets不仅适用于C ++,而且具有python,perl,php,java,lua,lisp,erlang,eiffel,C#(.NET),BASIC,ruby甚至ja ...

  5. 怎样查看python的所有关键字

    关键字是python中具有特定功能的一组词汇, 这些词汇不能用作变量名, 一般会有高亮提示, code时请小心. python的关键字其实也是python的语法核心, 掌握了所有python关键字的用 ...

  6. dfs的剪枝优化

    两个剪枝问题 1. 当两点的距离(需要走的步数)大于剩下的时间时 剪去 2.奇偶剪枝问题 如果起点到终点所需走的步数的奇偶性与时间奇偶性不同的时候 剪去 起点到终点步数的奇偶性的判断 首先 明确点的奇 ...

  7. AngularJS在IE下页面缓存问题

    问题: 在使用AngularJS发出请求(GET)获取服务端数据,然后再绑定到页面中,你会发现在IE中总是显示原来的数据结果.这时候我们就会知道,IE做了缓存. 解决办法: 我们可以在AngularJ ...

  8. RabbitMQ 应用一

    (百度百科)MQ全称为Message Queue,消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.消息传递指的 ...

  9. ASP.NET Core利用拦截器 IActionFilter实现权限控制

    “麦荻网教系统”采用了前后端代码分离的架构,即“Miidy.Cloud.Console”站与“Miidy.Cloud.Manage”站(两个前端站)同时通过web api的方式调用“Miidy.Clo ...

  10. ORA-01790 错误处理 SQL同一数据库中,两个查询结果数据类型不同时的union all 合

    转自: 出现这种错误,要先看一下是不是sql中有用到连接:union,unio all之类的,如果有,需要注意相同名称字段的数据类型一定要相同. 所以在union 或者union all 的时候造成了 ...