文章目录
算法内容
主要解决:静态子树查询问题。
步骤
遍历一个节点 ,我们按以下的步骤进行遍历:
- 先遍历 u u u 的轻(非重)儿子,并计算答案,但 不保留遍历后它对 u u u影响;
- 遍历它的重儿子,保留它对 u u u 的影响;
- 再次遍历 u u u 的轻儿子的子树结点,加入这些结点的贡献,以得到 u u u 的答案。
主要代码
void get_data(int u){
// 对于u的子树遍历问题 转换为的欧拉序 L[u] ~ R[u];
for(int i = L[u];i<=R[u];i++){
int t = index[i];
//重儿子信息已经记录
if(index[i] == skp){
i = R[index[i]];
continue;
}
ans[u] = …………;
}
}
void dsu(int u){
//遍历轻儿子
for(int i = h[u];~i;i=ne[i]){
int y = e[i];
if(y == fa[u] || y == son[u])continue;
dsu(y);
}
//遍历重儿子
if(son[u]){
dsu(son[u]);
skp = son[u];
}
//处理该节点的值
get_data(u);
//不为重儿子 清除.
if(u == top[u]){
//clear();
skp = 0;
}
}
例题
例题1 树的果实 (赛氪)
例题2 DongDong数颜色
统计子树不同的颜色个数
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5+10,M = N*2;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
ne[idx] = h[a],e[idx] = b,h[a] = idx++;
}
int n,m;
int a[N];
int col[N],ans[N];
int L[N],R[N],dfn,Index[N];
int top[N],son[N],siz[N],fa[N];
void dfs1(int u,int f){
L[u] = ++dfn;
Index[dfn] = u;
fa[u] = f;
siz[u] = 1;
for(int i = h[u];~i;i=ne[i]){
int y = e[i];
if(y == f)continue;
dfs1(y,u);
siz[u] += siz[y];
if(siz[son[u]] < siz[y])son[u] = y;
}
R[u] = dfn;
}
void dfs2(int v,int u){
top[v] = u;
if(!son[v])return ;
dfs2(son[v],u);
for(int i = h[v];~i;i=ne[i]){
int y = e[i];
if(y != fa[v] && y != son[v])dfs2(y,y);
}
}
int skp,tot;
void get_data(int x,int op){
// cout<< " x: "<< x << endl;
for(int i = L[x];i<=R[x];i++){
if(Index[i] == skp){
i = R[Index[i]];
continue;
}
// cout << Index[i] << " " << endl;
if(op==1 && ++col[a[Index[i]]] == 1)tot ++;
if(op==-1 && --col[a[Index[i]]] == 0)tot --;
}
// cout << endl;
}
void dsu(int x){
for(int i = h[x];~i;i=ne[i]){
int y = e[i];
if(y == son[x] || y == fa[x])continue;
dsu(y);
}
if(son[x]){
dsu(son[x]);
skp = son[x];
}
get_data(x,1);
//cout << "x: " << x << " "<< tot << endl;
ans[x] = tot;
if(x == top[x]){
skp = 0;
get_data(x,-1);
}
}
int main(){
cin >> n >> m;
for(int i = 1;i<=n;i++){
cin >> a[i];
}
memset(h,-1,sizeof h);
for(int i = 1;i<n;i++){
int a,b;
cin >> a >> b;
add(a,b),add(b,a);
}
dfs1(1,0);
dfs2(1,0);
dsu(1);
while(m--){
int a;
cin >> a;
cout << ans[a] << endl;
}
return 0;
}
例题3 D. Tree Requests
题意:
给你一颗树,顶点的权值为 a a a ~ z z z的字母,对于顶点 u u u找深度为 d d d的顶点能不能组成回文。 (这个深度为从根开始的.);
思路:
暴力维护
c
n
t
[
d
e
p
]
[
c
h
a
r
]
cnt[dep][char]
cnt[dep][char] 深度为
d
e
p
dep
dep的
c
h
a
r
char
char的个数.
对于是否能够组成回文:要么字母全为偶数,要么只有一个为奇数。(亦或运算)。
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N = 5e5+10,M = N*2;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
ne[idx] = h[a],e[idx] = b,h[a] = idx++;
}
int n,m;
struct node{
int id,dp;
};
vector<node> q[N];
int dep[N];
char c[N];
int col[N][30];
long long ans[N];
int L[N],R[N],dfn,Index[N];
int top[N],son[N],siz[N],fa[N];
void dfs1(int u,int f){
L[u] = ++dfn;
Index[dfn] = u;
fa[u] = f;
siz[u] = 1;
dep[u] = dep[f] + 1;
for(int i = h[u];~i;i=ne[i]){
int y = e[i];
if(y == f)continue;
dfs1(y,u);
siz[u] += siz[y];
if(siz[son[u]] < siz[y])son[u] = y;
}
R[u] = dfn;
}
void dfs2(int v,int u){
top[v] = u;
if(!son[v])return ;
dfs2(son[v],u);
for(int i = h[v];~i;i=ne[i]){
int y = e[i];
if(y != fa[v] && y != son[v])dfs2(y,y);
}
}
int skp,tot;
void add(int x){
for(int i = L[x]+1;i<=R[x];i++){
if(Index[i] == skp){
col[dep[skp]][c[skp] - 'a'] ^= 1;
i = R[Index[i]];
continue;
}
//col(x,y) 深度为x,字母为y的个数
col[dep[Index[i]]][c[Index[i]] - 'a'] ^= 1;
}
}
void sub(int x){
for(int i = L[x];i<=R[x];i++){
col[dep[Index[i]]][c[Index[i]] - 'a'] = 0;
}
}
void dsu(int x){
for(int i = h[x];~i;i=ne[i]){
int y = e[i];
if(y == son[x] || y == fa[x])continue;
dsu(y);
}
if(son[x]){
dsu(son[x]);
skp = son[x];
}
add(x);
for(auto s : q[x]){
int d = s.dp,id = s.id;
for(int i = 0;i<26;i++){
// cout << char(i+'a') << col[d][i] << endl;
ans[id] += col[d][i];
}
}
if(x == top[x]){
skp = 0;
sub(x);
}
}
int main(){
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i = 2,a;i<=n;i++){
scanf("%d",&a);
add(i,a),add(a,i);
}
getchar();
for(int i = 1;i<=n;i++){
scanf("%c",&c[i]);
}
for(int i = 1;i<=m;i++){
int u,d;
scanf("%d%d",&u,&d);
q[u].push_back({i,d});
}
dfs1(1,0);
dep[0] = -1;
dfs2(1,0);
dsu(1);
for(int i = 1;i<=m;i++){
if(ans[i] <= 1)printf("Yes\n");
else printf("No\n");
}
return 0;
}
例题:E. Blood Cousins Return
题目:
和上一题差不多。
题目给的是森林,每个顶点有个
n
a
m
e
name
name,求对于顶点
u
u
u深度为
d
d
d的不同
n
a
m
e
name
name的个数.
(这个
d
d
d是相对于
u
u
u)
思路:
建立一个原点
1
1
1,将森林变成一颗树。
对于深度
d
d
d可以加上
u
u
u的深度。
求不同name的个数和上一题一样了。
注意:
- + d e p +dep +dep时会越界(数组开到 2 e 5 2e5 2e5)
- 计数数组用map.
数组:
d
p
[
x
]
:
dp[x]:
dp[x]:深度为
x
x
x的不同
n
a
m
e
name
name的个数。
c
o
l
[
x
]
[
y
]
:
col[x][y] :
col[x][y]:深度为
x
x
x的
n
a
m
e
name
name为
y
y
y的个数。
代码:
#include<iostream>
#include<cstring>
#include<vector>
#include<map>
using namespace std;
const int N = 2e5+10,M = N*2;
int h[N],e[M],ne[M],idx;
void add(int a,int b){
ne[idx] = h[a],e[idx] = b,h[a] = idx++;
}
int n,m;
struct node{
int id,dp;
};
map<string,int> mp;
map<int,int> col[N];
vector<node> q[N];
int dep[N];
int c[N];
long long ans[N];
int L[N],R[N],dfn,Index[N];
int top[N],son[N],siz[N],fa[N];
int cnt;
void dfs1(int u,int f){
L[u] = ++dfn;
Index[dfn] = u;
fa[u] = f;
siz[u] = 1;
dep[u] = dep[f] + 1;
for(int i = h[u];~i;i=ne[i]){
int y = e[i];
if(y == f)continue;
dfs1(y,u);
siz[u] += siz[y];
if(siz[son[u]] < siz[y])son[u] = y;
}
R[u] = dfn;
}
void dfs2(int v,int u){
top[v] = u;
if(!son[v])return ;
dfs2(son[v],u);
for(int i = h[v];~i;i=ne[i]){
int y = e[i];
if(y != fa[v] && y != son[v])dfs2(y,y);
}
}
int skp,tot;
int dp[N];
void add(int x){
for(int i = L[x]+1;i<=R[x];i++){
// cout << " x : " << x << " index : " << Index[i] << endl;
if(Index[i] == skp){
if( ++ col[dep[skp]][c[skp]] == 1) dp[dep[skp]] ++;
// cout << dp[dep[Index[i]]] << endl;
i = R[Index[i]];
continue;
}
//col(x,y) : 表示深度为x,id为y的个数
//cout << "name : "<< c[Index[i]] << endl;
// cout << col[dep[Index[i]]][c[Index[i]]] << endl;
if( ++ col[dep[Index[i]]][c[Index[i]]] == 1) dp[dep[Index[i]]] ++;
//cout << dp[dep[Index[i]]] << endl;
}
}
void sub(int x){
for(int i = L[x]+1;i<=R[x];i++){
if( -- col[dep[Index[i]]][c[Index[i]]] == 0) dp[dep[Index[i]]] --;
//cout << "sub L " << col[dep[Index[i]]][c[Index[i]]] << endl;
}
}
void dsu(int x){
for(int i = h[x];~i;i=ne[i]){
int y = e[i];
if(y == son[x] || y == fa[x])continue;
dsu(y);
}
if(son[x]){
dsu(son[x]);
skp = son[x];
}
add(x);
for(auto s : q[x]){
int d = s.dp,id = s.id;
// cout << x << " " << d << " " << id << endl;
// cout << dp[d] << endl;
ans[id] = dp[d];
}
if(x == top[x]){
skp = 0;
// cout << "insub : " << x << endl;
sub(x);
}
}
int main(){
cin >> n ;
memset(h,-1,sizeof h);
for(int i = 2;i<=n+1;i++){
string name;
int f;
cin >> name >> f;
if(!mp[name])mp[name] = ++cnt;
c[i] = mp[name];
if(f == 0)add(1,i),add(i,1);
else add(i,f+1),add(f+1,i);
}
dfs1(1,0);
dep[0] = -1;
dfs2(1,0);
cin >> m;
for(int i = 1;i<=m;i++){
int u,d;
scanf("%d%d",&u,&d);
u++;
q[u].push_back({i,d+dep[u]});
}
dsu(1);
for(int i = 1;i<=m;i++){
cout << ans[i] << endl;
}
return 0;
}
例题 E. Blood Cousins
题意:
求第
u
u
u个节点的第
d
d
d层表亲个数,表亲定义:如果
u
u
u,
v
v
v的向上
d
d
d层是
z
z
z,则
u
,
v
u,v
u,v互为表亲。
思路:
找
u
u
u的
d
d
d级祖先,就转换为上个题了。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int N = 1e5+10,M = 2e5+10;
int h[N],e[M],ne[M],idx;
struct node{
int id,d;
};
vector<node> q[N];
int n,m;
int ans[N];
void add(int a,int b){
ne[idx] = h[a],e[idx] = b,h[a] = idx++;
}
int dep[N],siz[N],top[N],fa[N][22],son[N];
int L[N],R[N],Index[N],dfn;
int cnt[N],skp;
void dfs1(int u,int f){
dep[u] = dep[f] + 1;
fa[u][0] = f;
siz[u] = 1;
L[u] = ++dfn;
Index[dfn] = u;
for(int i = 1;i<=20;i++) fa[u][i] = fa[fa[u][i-1]][i-1];
for(int i = h[u];~i;i=ne[i]){
int y = e[i];
if(y == f)continue;
dfs1(y,u);
siz[u] += siz[y];
if(siz[son[u]] < siz[y])son[u] = y;
}
R[u] = dfn;
}
void dfs2(int u,int v){
top[u] = v;
if(!son[u])return ;
dfs2(son[u],v);
for(int i = h[u];~i;i=ne[i]){
int y = e[i];
if(y != son[u] && y != fa[u][0])dfs2(y,y);
}
}
int get(int x,int k){
if(!k)return x;
int id = log2(k);
return get(fa[x][id] , k - (1<<id));
}
void add(int u){
for(int i = L[u];i<=R[u];i++){
if(Index[i] == skp){
i = R[skp];
continue;
}
cnt[dep[Index[i]]]++;
}
}
void sub(int u){
for(int i = L[u];i<=R[u];i++){
cnt[dep[Index[i]]]=0;
}
}
int dsu(int u){
for(int i = h[u];~i;i=ne[i]){
int y = e[i];
if(y != fa[u][0] && y != son[u])dsu(y);
}
if(son[u]){
dsu(son[u]);
skp = son[u];
}
add(u);
for(auto s : q[u]){
int id = s.id,d = s.d;
ans[id] = cnt[d];
}
if(u == top[u]){
skp = 0;
sub(u);
}
}
int main(){
cin >> n ;
memset(h,-1,sizeof h);
for(int i = 2,a;i<=n+1;i++){
scanf("%d",&a);
if(a == 0)add(1,i),add(i,1);
else add(a+1,i),add(i,a+1);
}
dfs1(1,0);
dep[0] = -1;
dfs2(1,0);
cin >> m;
int a,b;
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
a++;
int fx = get(a,b);
if(fx == 0 || fx == 1)ans[i] = 1;
else
q[fx].push_back({i,dep[fx] + b});
}
dsu(1);
for(int i = 1;i<=m;i++)cout << ans[i] - 1 << " ";
return 0;
}
over