线段树合并
线段树合并
前言:
不知道为什么最近做题总是能做到线段树合并的题目
前置知识:
权值线段树、线段树动态开点
做法
假设我们要把\(Y\)树合并到X树上,对于\(X\)树上的每一个点
- 如果这个点有儿子,\(Y\)树上对应的点有,继续便利;
- 如果这个点有儿子,\(Y\)树上对应的点没有,直接跳过;
- 如果这个点没有儿子,\(Y\)树上对应的点有,因为我们是动态开点,所以可以直接把\(X\)树上这个点的儿子设为Y树上对应点儿子的编号
- 两棵树都没有就不管了
听tjm大佬说线段树合并最好开1e7或5e6?
例题:
P3605 [USACO17JAN]Promotion Counting P
线段树合并版题 但是要先离散化一下我好像还不会,做一棵权值线段树,然后从叶子节点到根节点做一遍\(dfs\)
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int fa[N] , cnt , hd[N] , a[N] , ans[N] , n , ps , num[N];
struct E {
int to,nt;
} e[N];
struct Tr {
int l,r,v;
} tr[1900006];
struct R {
int id,val;
} re[N];
void add(int x,int y) {
e[++cnt].to=y;
e[cnt].nt=hd[x];
hd[x]=cnt;
}
void updata(int x) {
tr[x].v = tr[tr[x].l].v + tr[tr[x].r].v;
}
void build(int p,int l,int r,int val) {
if(l == r) {
tr[p].v = 1;
}
else {
int mid=l + r >> 1;
if(val <= mid) {
if(!tr[p].l) {
tr[p].l = ++ps;
}
build(tr[p].l,l,mid,val);
}
else {
if(!tr[p].r) {
tr[p].r = ++ps;
}
build(tr[p].r,mid+1,r,val);
}
tr[p].v = 1;
updata(p);
}
}
void Marge(int x,int y,int l,int r) {
if(l == r) {
tr[x].v+=tr[y].v;
}
else {
int mid=l + r >> 1;
if(!tr[x].l && tr[y].l) {
tr[x].l = tr[y].l;
}
else if(tr[x].l && tr[y].l) {
Marge(tr[x].l,tr[y].l,l,mid);
}
if(!tr[x].r && tr[y].r) {
tr[x].r = tr[y].r;
}
else if(tr[x].r && tr[y].r) {
Marge(tr[x].r,tr[y].r,mid+1,r);
}
updata(x);
}
}
int query(int p,int l,int r,int val) {
if(l == r) {
return 0;
}
else {
int mid=l+r>>1 , sum=0;
if(val <= mid) {
if(tr[p].l)
sum += query(tr[p].l,l,mid,val) + tr[tr[p].r].v;
}
if(val > mid) {
if(tr[p].r)
sum += query(tr[p].r,mid+1,r,val);
}
return sum;
}
}
void fans(int x) {
int y;
for(int i=hd[x];i;i=e[i].nt) {
y=e[i].to;
fans(y);
Marge(x,y,1,n);
}
ans[x] = query(x,1,n,a[x]);
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
num[i]=a[i];
}
sort(num+1,num+n+1);
int t=unique(num+1,num+n+1)-num-1;
for(int i=1;i<=n;i++) {
a[i]=lower_bound(num+1,num+t+1,a[i])-num;
}
ps=t;
for(int i=1;i<=n;i++) {
build(i,1,n,a[i]);
}
for(int i=2;i<=n;i++) {
scanf("%d",&fa[i]);
add(fa[i],i);
}
fans(1);
for(int i=1;i<=n;i++) {
printf("%d\n",ans[i]);
}
return 0;
}
[HNOI2012]永无乡
雨天的尾巴
如果人生会有很长,愿有你的荣耀永不散场