HDU 5111 Alexandra and Two Trees 树链剖分 + 主席树

时间:2021-12-04 22:32:43

题意:

给出两棵树,每棵树的节点都有一个权值。

同一棵树上的节点的权值互不相同,不同树上节点的权值可以相同。

要求回答如下询问:

  • \(u_1 \, v_1 \, u_2 \, v_2\):询问第一棵树的路径\(u_1 \to v_1\)的节点权值 与 第一棵树的路径\(u_2 \to v_2\)的节点权值的交集的大小。

分析:

通用的思路是这样的:首先解决线性区间上的问题,然后用树链剖分来将树上问题转化为线性问题。

考虑两个序列之间求交集:给出两个序列\(S_1, S_2\),\(S_1\)有\(n_1\)个元素\(a_1, a_2, \cdots , a_{n_1}\),\(S_2\)有\(n_2\)个元素\(b_1, b_2, \cdots , b_{n_2}\)。

每次询问\(S_1\)的子区间\([l_1,r_1]\)和\(S_2\)的子区间\(l_2,r_2\)的交集的大小。

首先定义一个函数\(f\)把\(a_1 \sim a_{n_1}\)映射为\(1 \sim n_1\)

同样地,如果\(b_i\)在\(S_1\)中出现另\(b_i=f(b_i)\),否则另\(b_i=0\)

这样就将问题转化为求\(S_2\)的子区间\([l_2,r_2]\)中值在\([l_1, r_1]\)范围中元素的个数。

因此可以用线段树来解决。

回到本问题,先把第一棵树剖分,路径\(u_1 \to v_1\)就变成若干个连续的区间。

再对第二棵树建一棵主席树,维护的是根节点到当前节点对应区间的元素的个数。

对于每个区间,查询一次在这个区间内路径\(u_2 \to v_2\)上在这个区间内的元素的个数。

处理每次询问的复杂度为\(O(log^2n)\)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#define MP make_pair
#define F first
#define S second
using namespace std; typedef pair<int, int> PII;
const int maxn = 100000 + 10; struct Edge
{
int v, nxt;
Edge() {}
Edge(int v, int nxt): v(v), nxt(nxt) {}
}; struct Tree
{
int n, w[maxn];
int ecnt, head[maxn];
Edge edges[maxn];
int fa[maxn], dep[maxn], sz[maxn], son[maxn]; void init() { ecnt = 0; memset(head, -1, sizeof(head)); } void AddEdge(int u, int v) {
edges[ecnt] = Edge(v, head[u]);
head[u] = ecnt++;
} bool read() {
if(scanf("%d", &n) != 1) return false;
init();
for(int u = 2; u <= n; u++) {
scanf("%d", fa + u);
AddEdge(fa[u], u);
}
for(int i = 1; i <= n; i++) scanf("%d", w + i);
return true;
} void dfs(int u) {
sz[u] = 1; son[u] = 0;
for(int i = head[u]; ~i; i = edges[i].nxt) {
int v = edges[i].v;
dep[v] = dep[u] + 1;
dfs(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
}; Tree t1, t2; int n1, n2;
int x[maxn]; //Heavy Light Decomposition
int tot;
int id[maxn], pos[maxn], top[maxn]; void dfs2(int u, int tp) {
id[u] = ++tot;
int p = lower_bound(x + 1, x + 1 + n1, t1.w[u]) - x;
pos[p] = tot;
top[u] = tp;
if(!t1.son[u]) return;
dfs2(t1.son[u], tp);
for(int i = t1.head[u]; ~i; i = t1.edges[i].nxt) {
int v = t1.edges[i].v;
if(v == t1.son[u]) continue;
dfs2(v, v);
}
} vector<PII> inter; void getIntervals(int u, int v) {
inter.clear();
while(top[u] != top[v]) {
if(t1.dep[top[u]] < t1.dep[top[v]]) swap(u, v);
inter.push_back(MP(id[top[u]], id[u]));
u = t1.fa[top[u]];
}
if(t1.dep[u] > t1.dep[v]) swap(u, v);
inter.push_back(MP(id[u], id[v]));
} //Least Common Ancestor
int anc[maxn][20]; void preprocess() {
memset(anc, 0, sizeof(anc));
for(int i = 1; i <= n2; i++) anc[i][0] = t2.fa[i];
for(int j = 1; (1 << j) < n2; j++)
for(int i = 1; i <= n2; i++) if(anc[i][j-1])
anc[i][j] = anc[anc[i][j-1]][j-1];
} int LCA(int u, int v) {
int log;
if(t2.dep[u] < t2.dep[v]) swap(u, v);
for(log = 0; (1 << log) < t2.dep[u]; log++);
for(int i = log; i >= 0; i--)
if(t2.dep[u] - (1<<i) >= t2.dep[v]) u = anc[u][i];
if(u == v) return u;
for(int i = log; i >= 0; i--)
if(anc[u][i] && anc[u][i] != anc[v][i])
u = anc[u][i], v = anc[v][i];
return t2.fa[u];
} //Functional Segment Tree
const int maxnode = maxn << 5; int sz, root[maxn];
int lch[maxnode], rch[maxnode], sum[maxnode]; int update(int pre, int L, int R, int p) {
int rt = ++sz;
sum[rt] = sum[pre] + 1;
if(L < R) {
int M = (L + R) / 2;
if(p <= M) { rch[rt] = rch[pre]; lch[rt] = update(lch[pre], L, M, p); }
else { lch[rt] = lch[pre]; rch[rt] = update(rch[pre], M+1, R, p); }
}
return rt;
} void build(int u, int p) {
if(!t2.w[u]) root[u] = root[p];
else root[u] = update(root[p], 1, n1, t2.w[u]);
for(int i = t2.head[u]; ~i; i = t2.edges[i].nxt) {
int v = t2.edges[i].v;
build(v, u);
}
} int query(int u, int v, int lca, int L, int R, int qL, int qR) {
if(qL <= L && R <= qR) { return sum[u] + sum[v] - sum[lca] * 2; }
int ans = 0;
int M = (L + R) / 2;
if(qL <= M) ans += query(lch[u], lch[v], lch[lca], L, M, qL, qR);
if(qR > M) ans += query(rch[u], rch[v], rch[lca], M+1, R, qL, qR);
return ans;
} int main()
{
while(t1.read()) {
t2.read();
n1 = t1.n; n2 = t2.n;
for(int i = 1; i <= n1; i++) x[i] = t1.w[i];
sort(x + 1, x + 1 + n1); t1.dfs(1); t2.dfs(1);
preprocess();
tot = 0; dfs2(1, 1); for(int i = 1; i <= n2; i++) {
int p = lower_bound(x + 1, x + 1 + n1, t2.w[i]) - x;
if(p < 1 || p > n2 || x[p] != t2.w[i]) t2.w[i] = 0;
else t2.w[i] = pos[p];
} sz = 1;
build(1, 0); int q; scanf("%d", &q);
while(q--) {
int u1, v1, u2, v2;
scanf("%d%d%d%d", &u1, &v1, &u2, &v2);
getIntervals(u1, v1);
int ans = 0;
int lca = LCA(u2, v2);
for(PII a : inter) {
if(a.F <= t2.w[lca] && t2.w[lca] <= a.S) ans++;
ans += query(root[u2], root[v2], root[lca], 1, n1, a.F, a.S);
}
printf("%d\n", ans);
}
} return 0;
}

HDU 5111 Alexandra and Two Trees 树链剖分 + 主席树的更多相关文章

  1. dfs序&plus;主席树 或者 树链剖分&plus;主席树(没写) 或者 线段树套线段树 或者 线段树套splay 或者 线段树套树状数组 bzoj 4448

    4448: [Scoi2015]情报传递 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 588  Solved: 308[Submit][Status ...

  2. Codechef FIBTREE 树链剖分 主席树 LCA 二次剩余 快速幂

    原文链接https://www.cnblogs.com/zhouzhendong/p/CC-FIBTREE.html 题目传送门 - CC-FIBTREE 题意 给定一个有 $n$ 个节点,初始点权都 ...

  3. BZOJ1146 &lbrack;CTSC2008&rsqb;网络管理Network 树链剖分 主席树 树状数组

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1146 题意概括 在一棵树上,每一个点一个权值. 有两种操作: 1.单点修改 2.询问两点之间的树链 ...

  4. bzoj 4448 &lbrack;Scoi2015&rsqb;情报传递 &lpar;树链剖分&plus;主席树)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4448 题面: Description 奈特公司是一个巨大的情报公司,它有着庞大的情报网络 ...

  5. BZOJ 4448&colon; &lbrack;Scoi2015&rsqb;情报传递 树链剖分 主席树

    4448: [Scoi2015]情报传递 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4448 Description 奈特公司是一个巨 ...

  6. &lbrack;GDOI2016&rsqb;&lbrack;树链剖分&plus;主席树&rsqb;疯狂动物城

    题面 Description Nick 是只在动物城以坑蒙拐骗为生的狐狸,儿时受到偏见的伤害,放弃了自己的理想.他被兔子 Judy 设下圈套,*与她合作查案,而卷入意想不到的阴谋,历尽艰险后成为搭档 ...

  7. BZOJ3531 SDOI2014 旅行 - 树链剖分&comma;主席树

    题意:给定一棵树,树上每个点有权值和类型.支持:修改某个点的类型:修改某个点的权值:询问某条链上某个类型的点的和/最大值.点数/类型数/询问数<=100000. 分析: 树链剖分,对每个类型的点 ...

  8. 5&period;15 牛客挑战赛40 E&Tab;小V和gcd树 树链剖分 主席树 树状数组 根号分治

    LINK:小V和gcd树 时限是8s 所以当时好多nq的暴力都能跑过. 考虑每次询问暴力 跳父亲 这样是nq的 4e8左右 随便过. 不过每次跳到某个点的时候需要得到边权 如果直接暴力gcd的话 nq ...

  9. BZOJ4012 HNOI2015开店(树链剖分&plus;主席树)

    考虑这样一个问题:一棵树初始全是白点,有两种操作:把一个点染黑:询问某点到所有黑点的距离之和. 注意到树上两点x和y的距离为depth[x]+depth[y]-depth[lca(x,y)]*2.要求 ...

随机推荐

  1. sqlite like 通配符 &comma;匹配区分大小写&lpar;默认不区分大小写&rpar;

    在查询前先执行这个语句 , 1 时区分大小写,0时不区分 PRAGMA case_sensitive_like =0; select prod_name,PROD_PRICEfrom products ...

  2. Java温故系列之web项目复习

    如果从外面导入项目 操作方法为: File->Import -->General-->Existing Project into Workspace 搭建sqlserver数据库连接 ...

  3. CSS 中背景图片定位方法

    三种: 关键字:background-position: top left; 像素:background-position: 0px 0px; 百分比:background-position: 0% ...

  4. 国内android帮助文档镜像网站---http&colon;&sol;&sol;wear&period;techbrood&period;com&sol;develop&sol;index&period;html

    http://wear.techbrood.com/develop/index.html

  5. Linux 驱动程序&sol;内核模块&sol;ko文件

    Linux 驱动程序/内核模块/ko文件 一.内核模块加载机制 1.解析 Linux 内核可装载模块的版本检查机制 二.驱动/内核模块 编译 1.The Linux Kernel Module Pro ...

  6. Windows Phone开发(13):如何规范用户的输入行为

    原文:Windows Phone开发(13):如何规范用户的输入行为 很多时候,我们对用户的操作或输入做一定程度的限制,以避免发生不必要的异常或错误,因此,对一些特殊的类型,进行输入限制是很有必要的. ...

  7. iOS基础 - iOS程序启动原理

    一.UIApplicationMain 在main.m的main函数中执行了UIApplicationMain这个方法,这是ios程序的入口点 int UIApplicationMain(int ar ...

  8. ML笔记-sklearn&period;classification&lowbar;report

    主要用于显示主要分类指标的文本报告,在报告中显示每个类的精确度.召回率.F1等信息 首先数据测试结果分为以下4种情况: TP:预测为正,实现为正 FP:预测为正,实现为负 FN:预测为负,实现为正 T ...

  9. C&num;数组--&lpar;一维数组,二维数组的声明,使用及遍历&rpar;

    数组:是具有相同数据类型的一组数据的集合.数组的每一个的变量称为数组的元素,数组能够容纳元素的数称为数组的长度. 一维数组:以线性方式存储固定数目的数组元素,它只需要1个索引值即可标识任意1个数组元素 ...

  10. django遇到的问题-系列1

    django开发中遇到的问题以及解决方法: 1.You called this URL via POST, but the URL doesn't end in a slash and you hav ...