zoukankan      html  css  js  c++  java
  • 神奇的树上启发式合并 (dsu on tree)

    参考资料

    https://www.cnblogs.com/zhoushuyu/p/9069164.html
    https://www.cnblogs.com/candy99/p/dsuontree.html
    https://www.cnblogs.com/zcysky/p/6822395.html

    简介

    树上启发式合并
    用到了heavy−light decomposition树链剖分
    把轻边子树的信息合并到重链上的点里
    因为每次都是先dfs轻儿子再dfs重儿子,只有重儿子子树的贡献保留,所以可以保证dfs到每颗子树时当前全局维护的信息不会有别的子树里的,和莫队很像

    算法实现

    1.遍历轻儿子
    2.遍历重儿子(保留数据)
    3.计算所有轻儿子为根的子树
    4.如果当前点是轻儿子,清空影响

    复杂度分析

    树链剖分后每个点到根的路径上有(logn)条轻边和(logn)条重链

    每个点遇见轻边时合并一次,所以至多(logn)

    总复杂度(O(nlogn))

    例题

    CF600E. Lomsat gelral

    http://codeforces.com/contest/600/problem/E
    题意:询问每颗子树中出现次数最多的颜色们编号和

    板子题

    Code

    #include<bits/stdc++.h>
    
    #define LL long long
    #define RG register
    
    using namespace std;
    template<class T> inline void read(T &x) {
    	x = 0; RG char c = getchar(); bool f = 0;
    	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    	x = f ? -x : x;
    	return ;
    }
    template<class T> inline void write(T x) {
    	if (!x) {putchar(48);return ;}
    	if (x < 0) x = -x, putchar('-');
    	int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    	for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
    }
    const int N = 1e5+10;
    int c[N], n;
    struct node {
    	int to, nxt;
    }g[N<<1];
    int last[N], gl;
    
    void add(int x, int y) {
    	g[++gl] = (node) {y, last[x]};
    	last[x] = gl;
    }
    int son[N], siz[N];
    void dfs1(int u, int f) {
    	siz[u] = 1;
    	for (int i = last[u]; i; i = g[i].nxt) {
    		int v = g[i].to;
    		if (v == f) continue;
    		dfs1(v, u);
    		siz[u] += siz[v];		
    		if (siz[son[u]] < siz[v]) son[u] = v;
    	}
    	return ;
    }
    
    int num[N], top;
    LL sum[N], ans[N];
    bool vis[N];
    
    void calc(int u, int fa, int k) {
    	sum[num[c[u]]] -= c[u];
    	num[c[u]] += k;
    	sum[num[c[u]]] += c[u];
    	if (sum[top + 1]) top++;
    	else if (!sum[top]) top--;
    
    	for (int i = last[u]; i; i = g[i].nxt) {
    		int v = g[i].to;
    		if (v == fa || vis[v]) continue;
    		calc(v, u, k);
    	}
    	return ;
    }
    
    void dfs(int u, int fa, int op) {
    	for (int i = last[u]; i; i = g[i].nxt)
    		if (g[i].to != fa && g[i].to != son[u])
    			dfs(g[i].to, u, 0);
    	if (son[u])
    		dfs(son[u], u, 1), vis[son[u]] = 1;
    	calc(u, fa, 1); vis[son[u]] = 0;
    	ans[u] = sum[top];
    	if (!op) calc(u, fa, -1);
    	return ;
    }
    
    
    int main() {
    	read(n);
    	for (int i = 1; i <= n; i++) read(c[i]);
    	for (int i = 1; i < n; i++) {
    		int x, y; read(x), read(y);
    		add(x, y), add(y, x);
    	}
    	dfs1(1, 0);
    	dfs(1, 0, 1);
    	for (int i = 1; i <= n; i++)
    		printf("%I64d ", ans[i]);
    	return 0;
    }
    
    

    CF570D Tree Requests

    http://codeforces.com/problemset/problem/570/D
    https://www.luogu.org/problemnew/show/CF570D

    构成回文串,奇数个的字母至多一个
    用二进制状压判断即可

    (sum[x])表示深度为(x)构成的状态

    #include<bits/stdc++.h>
    
    #define LL long long
    #define RG register
    
    using namespace std;
    template<class T> inline void read(T &x) {
    	x = 0; RG char c = getchar(); bool f = 0;
    	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    	x = f ? -x : x;
    	return ;
    }
    template<class T> inline void write(T x) {
    	if (!x) {putchar(48);return ;}
    	if (x < 0) x = -x, putchar('-');
    	int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    	for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
    }
    
    const int N = 500010;
    
    struct node {
    	int to, nxt;
    }g[N<<1], q[N];
    int last[N], gl;
    int n, m;
    void add(int x, int y) {
    	g[++gl] = (node) {y, last[x]};
    	last[x] = gl;
    	g[++gl] = (node) {x, last[y]};
    	last[y] = gl;
    }
    
    char s[N];
    int siz[N], son[N], val[N], dep[N], sum[N];
    bool vis[N];
    void dfs1(int u, int fa) {
    	siz[u] = 1;
    	for (int i = last[u]; i; i = g[i].nxt) {
    		int v = g[i].to;
    		if (v == fa) continue;
    		dep[v] = dep[u] + 1;
    		dfs1(v, u);		
    		siz[u] += siz[v];
    		if (siz[son[u]] < siz[v]) son[u] = v;
    	}	
    }
    
    void calc(int u, int fa) {
    	sum[dep[u]] ^= val[u];
    	for (int i = last[u]; i; i = g[i].nxt) {
    		int v = g[i].to;
    		if (v == fa || vis[v]) continue;
    		calc(v, u);
    	}
    	return ;
    }
    
    struct Node {
    	int h, nxt;
    }a[N];
    bool ans[N];
    int S[N];
    
    bool cal(int x) {
    	int tmp = 0;
    	while (x) {
    		tmp++;
    		x -= (x & (-x));
    	}
    	return tmp <= 1;
    }
    
    void dfs(int u, int fa, int op) {
    	for (int i = last[u]; i; i = g[i].nxt) {
    		int v = g[i].to;
    		if (v == fa || son[u] == v) continue;
    		dfs(v, u, 0);
    	}
    	if (son[u]) dfs(son[u], u, 1), vis[son[u]] = 1;
    	calc(u, fa); vis[son[u]] = 0;
    	for (int i = S[u]; i; i = a[i].nxt)
    		ans[i] = cal(sum[a[i].h]);
    	if (!op) calc(u, fa);
    	return ;
    }
    
    
    int main() {
    	read(n), read(m);
    	for (int i = 2; i <= n; i++) {
    		int x; read(x);
    		add(x, i);
    	}
    	scanf("%s", s+1);
    	for (int i = 1; i <= n; i++) val[i] = 1<<(s[i]-'a');
    	dep[1] = 1;
    	dfs1(1, 0);
    	for (int i = 1; i <= m; i++) {
    		int h, v;
    		read(v), read(h);
    		a[i].nxt = S[v];
    		S[v] = i; a[i].h = h;		
    	}
    	dfs(1, 0, 1);
    	for (int i = 1; i <= m; i++) puts(ans[i] ? "Yes" : "No");
    	return 0;
    }
    
    
  • 相关阅读:
    hdu 2065
    hdu 1999
    hdu 1562
    hdu 1728
    hdu 1180
    hdu 1088
    hdu 2133
    很好的例子。。
    putty 多标签式浏览
    df
  • 原文地址:https://www.cnblogs.com/zzy2005/p/10315931.html
Copyright © 2011-2022 走看看