树上启发式合并(DSU ON TREE)

算法过程

对于树上的一个节点 u u u,按照以下步骤执行:

  1. 遍历 u u u 的轻儿子,计算轻儿子的答案,不保留对 u u u 的贡献
  2. 遍历 u u u 的重儿子,计算重儿子的答案,保留对 u u u 的贡献
  3. 遍历 u u u 的轻儿子,加入其对 u u u 的贡献,得到 u u u 的答案

一般来说,没有修改、只对子树进行询问(或者转化成子树的答案) 就可以用 dsu on tree。

时间复杂度

根节点到任意节点的轻边数不超过 l o g n logn logn 条。设根节点 r o o t root root 到节点 u u u 的路径中有 x x x 条轻边,以 u u u 为根的子树大小为 y y y ,则 1 ≤ y < n / 2 x 1 \le y < n/2^x 1y<n/2x ,所以 x < l o g x x < logx x<logx 。重边不会被额外遍历。所以每个节点被遍历到的次数为 l o g n + 1 logn + 1 logn+1
时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

Lomsat gelral

题意:

询问子树内出现次数最多的颜色的编号和

代码:
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define int ll
const int N = 1e5+10;
int head[N], tot;
int son[N], size[N], pa[N];
int ans[N], cnt[N], col[N], sum, Max, Son;
int n;
struct edge{
	int to, nxt;
}e[N<<1];
void addedge(int a, int b){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	head[a] = tot;
}
inline int read(){//读入优化 
	char ch;
	while((ch=getchar())<'0'||ch>'9');
	int res = ch-48;
	while((ch=getchar())>='0'&&ch<='9')
		res = res*10+ch-48;
	return res;
}
void update(int x, int val){
	cnt[col[x]] += val;
	if(cnt[col[x]] > Max){
		Max = cnt[col[x]];
		sum = col[x];
	}
	else if(cnt[col[x]] == Max)
		sum += col[x];
	for(int i = head[x]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == pa[x] || v == Son)
			continue;
		update(v, val);
	}
}
void dfs1(int u, int fa){
	size[u] = 1;
	pa[u] = fa;
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == fa)
			continue;
		dfs1(v, u);
		size[u] += size[v];
		if(size[v] > size[son[u]])
			son[u] = v;
	}
}
void dfs2(int u, int flag){
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == pa[u] || v == son[u])
			continue;
		dfs2(v, 0);
	}
	if(son[u])
		dfs2(son[u], 1), Son = son[u];
	update(u, 1);
	ans[u] = sum; Son = 0;
	if(flag == 0)
		update(u, -1), sum = Max = 0;
}
signed main(){
	n = read();
	for(int i = 1; i <= n; i++)
		col[i] = read();
	for(int i = 1; i <= n-1; i++){
		int u, v;
		u = read(), v = read();
		addedge(u, v);
		addedge(v, u);
	}
	dfs1(1, 0);
	dfs2(1, 0);
	for(int i = 1; i <= n; i++)
		printf("%lld ", ans[i]);
	return 0;
}

Tree Requests

题意:

n n n 个节点的树,每个点上都有一个小写字母。m次询问,每次询问以 a a a 为根的子树内深度为 b b b 的节点重新排列后能否构成回文串(深度是在整棵树中的深度)

解析:

一个回文串中,只有一个字符能够出现奇数次。
对于以 u u u 为根的子树,处理所有的字符,记录每个深度下字符的数量。处理完之后解决在 u u u 子树中的询问

代码:
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 5E5+10;
int size[N], dep[N], son[N], pa[N], dfn[N], seg[N];
int s[N], cnt[N][30], ans[N];
int n, m, dfntot, tot, head[N];
inline int read(){//读入优化 
	char ch;
	while((ch=getchar())<'0'||ch>'9');
	int res = ch-48;
	while((ch=getchar())>='0'&&ch<='9')
		res = res*10+ch-48;
	return res;
}
struct node{
	int x, y;
};
vector<node> q[N];
struct edge{
	int to, nxt;
}e[N<<1];
inline void addedge(int a, int b){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	head[a] = tot;
}
inline void add(int x){
	cnt[dep[x]][s[x]]++;
}
inline void update(int x){
	for(int i = seg[x]; i <= seg[x]+size[x]-1; i++)
		add(dfn[i]);
}
inline void del(int x){
	cnt[dep[x]][s[x]] = 0;
}
void clear(int x){
	for(int i = seg[x]; i <= seg[x]+size[x]-1; i++)
		del(dfn[i]);
}
inline int check(int x){
	int res = 0;
	for(int i = 1; i <= 26; i++)
		if(cnt[x][i]&1)
			res++;
	return res <= 1;
}
void dfs1(int u, int fa){
	dep[u] = dep[fa]+1;
	size[u] = 1;
	seg[u] = ++dfntot;
	dfn[dfntot] = u;
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == fa)
			continue;
		dfs1(v, u);
		size[u] += size[v];
		if(size[v] > size[son[u]])
			son[u] = v;
	}
}
void dfs2(int u, int flag){
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v==son[u]||v==pa[u])
			continue;
		dfs2(v, 0);
	}
	if(son[u])
		dfs2(son[u], 1);
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == son[u]||v == pa[u])
			continue;
		update(v);
	}
	add(u);
	for(int i = 0; i < q[u].size(); i++)
		ans[q[u][i].y] = check(q[u][i].x);
	if(flag == 0)
		clear(u);
}
int main(){
	n = read(); m = read();
	for(int i = 2, x; i <= n; i++){
		x = read();
		pa[i] = x;
		addedge(x, i);
	}
	string ss; cin >> ss;
	for(int i = 1; i <= n; i++)
		s[i] = ss[i-1]-'a'+1;
	for(int i = 1, x, y; i <= m; i++){
		x = read(); y = read();
		q[x].push_back((node){y, i});
	}
	dfs1(1, 0);
	dfs2(1, 0);
	for(int i = 1; i <= m; i++){
		ans[i] ? puts("Yes") : puts("No");
	}
	return 0;
}

Blood Cousins

题意

给定一个森林,每次询问 ( v , p ) (v,p) (v,p),节点 v v v 的 p-cousin 的个数。若 u u u v v v 深度相同且 u u u l c a ( u , v ) lca(u,v) lca(u,v) 的距离小于 p p p ,则 u u u v v v 的 p-cousin

解析

对于询问 ( v , p ) (v,p) (v,p),找到到 v v v 的距离为 p p p 的祖先节点 f a fa fa ,在以 f a fa fa 为根的子树内,计算到 f a fa fa 距离为 p p p 节点的数量,然后减 1 1 1

代码
#include
#include
#include
using namespace std;
const int N = 1E5+10;
const int M = 1e5+10;
const int INF = 0x3f3f3f3f;
inline int read(){//读入优化 
	char ch;
	while((ch=getchar())<'0'||ch>'9');
	int res = ch-48;
	while((ch=getchar())>='0'&&ch<='9')
		res = res*10+ch-48;
	return res;
}
int head[N], dep[N], ans[N], dfn[N], rev[N];
int p[N][20], cnt[N];
int size[N], son[N];
int n, tot, dfntot, m;
struct edge{
	int to, nxt;
}e[N<<1];
void addedge(int a, int b){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	head[a] = tot;
}
struct node{
	int k, id;
};
vector<node> q[N];
void add(int x){
	cnt[dep[x]]++;
}
void update(int x){
	for(int i = dfn[x]; i <= dfn[x]+size[x]-1; i++)
		add(rev[i]);
}
void del(int x){
	cnt[dep[x]]  --;
}
void clear(int x){
	for(int i = dfn[x]; i <= dfn[x]+size[x]-1; i++)
		del(rev[i]);
}
int find_fa(int x, int y){
	for(int i = 18; i >= 0; i--){
		if(y >= (1<<i)){
			y -= (1<<i);
			x = p[x][i];
		}
	}
	return x;
}
void dfs1(int x, int fa){
	dep[x] = dep[fa]+1;
	size[x] = 1;
	dfn[x] = ++dfntot;
	rev[dfntot] = x;
	for(int i = 1; i <= 18; i++)
		p[x][i] = p[p[x][i-1]][i-1];
	for(int i = head[x]; i; i = e[i].nxt){
		int v = e[i].to;
		dfs1(v, x);
		size[x] += size[v];
		if(size[v] > size[son[x]])
			son[x] = v;
	}
}
void dfs2(int x, int flag){
	for(int i = head[x]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == son[x])
			continue;
		dfs2(v, 0);
	}
	if(son[x])
		dfs2(son[x], 1);
	for(int i = head[x]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == son[x])
			continue;
		update(v);
	}
	add(x);
	for(int i = 0; i < q[x].size(); i++)
		ans[q[x][i].id] = cnt[dep[x]+q[x][i].k];
	if(!flag)
		clear(x);
}
int main(){
	n = read();
	for(int i = 1, x; i <= n; i++){
		x = read();
		p[i][0] = x;
		addedge(x, i);
	}
	for(int i = 1; i <= n; i++)
		if(p[i][0] == 0)
			dfs1(i, 0);
	m = read();
	for(int i = 1; i <= m; i++){
		int x, y, z;
		x = read(), y = read();
		z = find_fa(x, y);
		if(z)
			q[z].push_back((node){y, i});
	}
	for(int i = 1; i <= n; i++){
		if(p[i][0] == 0)
			dfs2(i, 0);
	}
	for(int i = 1; i <= m; i++)
		printf("%d ", max(ans[i]-1, 0));
	return 0;
}

https://blog.csdn.net/qq_35975367/article/details/119134160
https://oi-wiki.org/graph/dsu-on-tree/

你可能感兴趣的:(图论,算法)