hdu 5111 树上求交(树链剖分 + 主席树)

题意:

给出两棵树,大小分别为\(n1\),\(n2\), 树上的结点权值为\(weight_i\)

同一棵树上的结点权值各不相同,不同树上的结点权值可以出现重复

每次查询 \(u1\) \(v1\) \(u2\) \(v2\)

第一棵树上\(u1\) 到 \(v1\)的路径上所有结点权值组成的集合\(S1\)

第二棵树上\(u2\) 到 \(v2\)的路径上所有结点权值组成的集合\(S2\)

求\(S1\) 与 \(S2\) 的交集

\(1 <= n1,n2 <= 10^{5}\)

\(q <= 50000\)

\(0 <= weight_i <= 10^{9}\)

\(1 <= u1,v1 <= n1, 1 <= u2,v2 <= n2\)

思路:题解

先考虑序列上的问题

将第二个序列的权值映射为相同权值在第一个序列中出现的下标

那么对于\(L1,R1,L2,R2\)来说

就是查询第二个序列中区间\([L2,R2]\)权值在\([L1,R1]\)的数字个数

这是经典问题,可以用主席树解决

现在考虑树上问题,

路径映射到区间是不连续的,用树链剖分处理,每段都是连续的,就可以用主席树来查询

主席树查询也是要求连续的,如果同样用树链剖分来处理复杂度有3个log了

由于不带修改,如果用dfs序的方式建主席树

就可以用root[u],root[v],root[lca]这些根的信息来处理表示u到v上的所有路径信息了

这样复杂度就变成两个log了

ps: 码力太弱了,写不动,写了大概两个半小时才写完,中间顺带复习了一波树链剖分

#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
#define ls(i) seg[i].lc
#define rs(i) seg[i].rc
using namespace std;
namespace IO {
const int MX = 4e7; //1e7 占用内存 11000kb
char buf[MX]; int c, sz;
void begin() {
c = 0;
sz = fread(buf, 1, MX, stdin);//一次性全部读入
}
inline bool read(int &t) {
while (c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
if (c >= sz) return false;//若读完整个缓冲块则退出
bool flag = 0; if(buf[c] == '-') flag = 1, c++;
for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';
if(flag) t = -t;
return true;
}
}
void read(int &x){
x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
}
const int N = 1e5 + 10; int n1,n2,x;
int value2[N];
map<int,int> pos; struct Edge{int v,nxt;}; ///主席树部分
struct T{
int lc,rc,cnt;
}seg[N * 50];
int root[N],tot;
void seg_init(){
root[0] = tot = 0;
seg[0].lc = seg[0].rc = seg[0].cnt = 0;
}
void update(int &rt,int l,int r,int v){
seg[++tot] = seg[rt];
rt = tot;
seg[rt].cnt++;
if(l >= r) return ;
int m = l + r>>1;
if(v <= m) update(ls(rt),l,m,v);
else update(rs(rt),m+1,r,v);
}
int query(int rt1,int rt2,int rt3,int l,int r,int pos){
if(r <= pos) return seg[rt1].cnt + seg[rt2].cnt - 2 * seg[rt3].cnt;
if(l > pos) return 0;
int m = l + r >>1;
return query(ls(rt1),ls(rt2),ls(rt3),l,m,pos) + query(rs(rt1),rs(rt2),rs(rt3),m+1,r,pos);
} int calc(int rt1,int rt2,int rt3,int L,int R){
return query(rt1,rt2,rt3,1,n1,R) - query(rt1,rt2,rt3,1,n1,L-1);
} ///dfs插入部分
Edge e[2 * N];
int head[N],EN;
int dep[N];
int fa[N][25];
void edge_init(){
EN = 0;
memset(head,-1,sizeof(head));
}
void add(int u,int v){
e[EN].v = v,e[EN].nxt = head[u];
head[u] = EN++;
}
void dfs(int u,int f,int d){
dep[u] = d,fa[u][0] = f;
root[u] = root[f];
if(value2[u]) update(root[u],1,n1,value2[u]);
for(int i = 1;i <= 20;i++) fa[u][i] = fa[fa[u][i-1]][i-1];
for(int i = head[u];~i;i = e[i].nxt){
int v = e[i].v;
if(v != f) dfs(v, u, d + 1);
}
}
int lca(int u,int v){
if(dep[u] < dep[v]) swap(u,v);
int d = dep[u] - dep[v];
for(int i = 20;i >= 0 && u != v;i--) if(d & (1<<i)) u = fa[u][i];
if(u == v) return u;
for(int i = 20;i >= 0;i--) if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i];
return fa[u][0];
} struct HLD{///树链剖分处理
int siz[N],dep[N],fa[N];
int top[N];///u所在链的第一个结点
int son[N];///以u为根的重儿子
int r[N];///每个位置是什么结点
int w[N];///结点u在哪个位置
int z;
int head[N],EN;
Edge e[2 * N];
void add(int u,int v){
e[EN].v = v,e[EN].nxt = head[u];
head[u] = EN++;
}
void init(){
EN = z = dep[1] = 0;
memset(head,-1,sizeof(head));
}
void dfs(int u,int f,int d){
fa[u] = f,dep[u] = d;
siz[u] = 1,son[u] = 0;
for(int i = head[u];~i;i = e[i].nxt){
int v = e[i].v;
if(v!=f){
dfs(v,u,d+1);
if(siz[v] > siz[son[u]]) son[u] = v;
siz[u] += siz[v];
}
}
}
void build(int u,int tp,int f){///当前结点所在链的第一个结点
w[u] = ++z,r[z] = u,top[u] = tp;
if(son[u]) build(son[u],tp,u);
for(int i = head[u];~i;i = e[i].nxt){
if(e[i].v != f && e[i].v != son[u]) build(e[i].v,e[i].v,u);
}
}
int solve(int u,int v,int c,int d){
int cd = lca(c,d);
int ans = 0;
int f1 = top[u],f2 = top[v];
while(f1 != f2){
if(dep[f1] < dep[f2]) swap(f1,f2),swap(u,v);
ans += calc(root[c],root[d],root[cd],w[f1],w[u]);
if(value2[cd] >= w[f1] && value2[cd] <= w[u]) ans++;
u = fa[f1],f1 = top[u];
}
if(dep[u] > dep[v]) swap(u,v);
ans += calc(root[c],root[d],root[cd],w[u],w[v]);
if(value2[cd] >= w[u] && value2[cd] <= w[v]) ans++;
return ans;
}
}hld; void init(){
edge_init();
hld.init();
seg_init();
pos.clear();
}
int main(){
IO::begin();
while(IO::read(n1)){
init();
for(int i = 2;i <= n1;i++){
IO::read(x);
hld.add(x,i);
}
hld.dfs(1,0,0);
hld.build(1,1,0);
for(int i = 1;i <= n1;i++) {
IO::read(x);
pos[x] = i;
}
IO::read(n2);
for(int i = 2;i <= n2;i++){
IO::read(x);
add(x,i);
}
for(int i = 1;i <= n2;i++) {
IO::read(x);
if(pos[x]) value2[i] = hld.w[pos[x]];
else value2[i] = 0;
}
dfs(1,0,0);
int a,b,c,d,q;
IO::read(q);
while(q--){
IO::read(a),IO::read(b),IO::read(c),IO::read(d);
printf("%d\n",hld.solve(a,b,c,d));
}
}
return 0;
}

hdu 5111 树上求交的更多相关文章

  1. HDU - 3982:Harry Potter and J.K.Rowling(半平面交+圆与多边形求交)(WA ing)

    pro:给定一枚蛋糕,蛋糕上某个位置有个草莓,寿星在上面切了N刀,最后寿星会吃含有草莓的那一块蛋糕,问他的蛋糕占总蛋糕的面积比. sol:显然需要半平面交求含有蛋糕的那一块,然后有圆弧,不太方便求交. ...

  2. ray与triangle/quad求交二三事

    引擎中,ray与quad求交,算法未细看,但有求解二次方程,不解.ray与triangle求交,使用的是97年经典算法,仔细看过论文,多谢小武同学指点,用到了克拉默法则求解线性方程组.想模仿该方法,做 ...

  3. [NetTopologySuite](2)任意多边形求交

    任意多边形求交: private void btnPolygon_Click(object sender, EventArgs e) { , , , , , , , , , , , , , }; , ...

  4. HDU 1524 树上无环博弈 暴力SG

    一个拓扑结构的图,给定n个棋的位置,每次可以沿边走,不能操作者输. 已经给出了拓扑图了,对于每个棋子找一遍SG最后SG和就行了. /** @Date : 2017-10-13 20:08:45 * @ ...

  5. HDU 3094 树上删边 NIM变形

    基本的树上删边游戏 写过很多遍了 /** @Date : 2017-10-13 18:19:37 * @FileName: HDU 3094 树上删边 NIM变形.cpp * @Platform: W ...

  6. hihocoder[Offer收割]编程练习赛19 D 相交的铁路线(树上路径交)

    傻逼题... 裸的树上路径交 两条树上的路径$[a,b]$和$[c,d]$有交,则有$lca(a,b)$在$[c,d]$上或$lca(c,d)$在$[a,b]$上. 其实只要深度大的$lca$在另一条 ...

  7. 光线求交-面、三角形、球 (Ray intersection)

    光线求交 光线定义:position \(a(t)\) = \(o\) + \(t\vec{d}\); 球定义: center p, radius r; 平面定义:normal \(\vec{n}\) ...

  8. 一步一步实现基于GPU的pathtracer(二):求交算法

    不管是哪种全局光照算法,最根本的都要落实到光线与物体的求交.主要分为光线与参数曲面和非参数曲面的求交,典型的参数曲面有球.盒.圆柱等基本体及基本体的组合体,以及一些更为复杂的参数曲面.非参数曲面就是所 ...

  9. OpenCASCADE直线与平面求交

    OpenCASCADE直线与平面求交 在<解析几何>相关的书中都给出了直线和平面的一般方程和参数方程.其中直线的一般方程有点向式形式的. 由于过空间一点可作且只能作一条直线平行于已知直线, ...

随机推荐

  1. 【杂题总汇】UVa-1336 Fixing the Great Wall

    [UVA-1336]Fixing the Great Wall 一开始把题看错了……直接用的整数存储答案:之后用double存最后输出答案的时候取整就AC了

  2. hibernate映射实体类查询时数据库空字段赋值给实体类报错的问题

    因为一直报实体类空异常,去网上查了资料只查到了并没有查到数据库空值时不给实体类赋值的属性 异常 org.hibernate.InvalidMappingException: Could not par ...

  3. 关于对GitHub的使用

    什么是GitHub? GitHub是版本控制和协作的代码托管平台.它可以让你在其他人在任何地方一起工作. 本文主要向您介绍GitHub essentials,如存储库,分支,提交和合并请求.将您创建自 ...

  4. 通信服务器哈希Socket查找(Delphi)

    在Socket通信服务器的开发中,我们经常会需要Socket与某个结构体指针进行绑定.当连接量很大时,意味着需要个高效的查找方法 Delphi中提供了哈希算法类,以此类为基础,修改出Socket专用M ...

  5. OLAP和OLTP

    OLTP与OLAP的介绍 数据处理分为两种技术架构系统:OLTP与OLAP OLTP(联机事务处理过程) OLTP是传统的关系型数据库的主要应用,主要是基本的,日常的事务处理,例如银行的交易     ...

  6. python 初学函数

    #len # s = '金老板小护士' # len(s) # def my_len(): #自定义函数 # i = 0 # for k in s: # i += 1 # print(i) # # le ...

  7. poj 1759 二分搜索

    题意:N个等差数列,初项X_i,末项Y_i,公差Z_i,求出现奇数次的数? 思路: 因为只有一个数出现的次数为奇数个 假设 第二个数字的个数为 奇数个,其余全部都是偶数个 ,累计出现的次数 a1偶数 ...

  8. android gradle.properties

    gradle.properties 里面配置的东西,在gradle 文件里面可以直接引用. 例如: 在你工程根目录的gradle.properties 文件里面 可以这样配置: ## Project- ...

  9. JWT应用

    调试器库简介问一件T恤! 精心制作 JSON Web令牌简介 新:免费获得JWT手册并深入学习JWT! 什么是JSON Web Token? JSON Web Token(JWT)是一个开放标准(RF ...

  10. Java - 收藏集 -

    Java - 收藏集 -   Java 基础思维导图,让 Java 不再难懂 - 工具资源 - 掘金思维导图的好处 最近看了一些文章的思维导图,发现思维导图真是个强大的工具.了解了思维导图的作用之后, ...