【树上问题杂谈】

简介

好,我摸爬滚打了好几天,干掉了数论
接下来找树了(
可能主要是一些树上维护信息,树形\(DP\)放到以后

主体

树上问题的一些基本方法

  1. \(LCA\)
  2. \(DFS\)
  3. 链差分
  4. 树形\(DP\)

\(LCA\)

  1. 树链剖分(好打且复杂度较优)
  2. \(RMQ\)(这个我不会)
  3. 倍增
    点权和:\(d_u + d_v - d_l - d_{p_l}\)
    边权和: \(d_u + d_v - 2 * d_l\)

\(DFS\)

\(DFS\)时计入一个点进入搜索树的时间点叫做\(DFS\)序,一颗子树对应的是\(DFS\)上的一个区间,如果也记录离开搜索树的时间戳,叫做欧拉序或括号序
括号序和\(DFS\)序,可以用来\(O(1)\)的判断一个点是否是另外一个点的子树。

树链剖分

把线段树可做的东西搬到树上,区间操作变成链操作和子树操作
同时树链剖分支持换根

#include<iostream>
#include<cstdio>
#define ll long long

struct P{
	ll to,next;
}e[400000];

ll n,m,root,p;
ll v[200000],head[200000];
ll cnt = 0;

void add(ll x,ll y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

int dep[200000],fa[200000],siz[200000],gson[200000];

int dfncnt,dfn[200000],top[200000],num[200000];

void dfs2(int u,int t){
	top[u] = t;
	dfn[u] = ++dfncnt;
	num[dfncnt] = v[u];
	if(!gson[u])return;
	dfs2(gson[u],t);
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa[u] || v == gson[u])
		continue;
		dfs2(v,v);
	}
}

void dfs(ll u,ll f){
	fa[u] = f;
	dep[u] = dep[f] + 1;
	siz[u] = 1;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == f)
		continue;
		dfs(v,u);
		siz[u] += siz[v];
		if(siz[v] > siz[gson[u]])
		gson[u] = v;
	}
}

struct Seg{
	struct E{
		ll l,r,v,m;
	}t[400000];
	#define l(x) t[x].l
	#define r(x) t[x].r
	#define v(x) t[x].v
	#define m(x) t[x].m
	#define mid ((l + r) >> 1)
	void up(ll now){v(now) = (v(l(now)) + v(r(now))) % p;}
	void push(ll now,ll l,ll r){
	m(l(now)) += m(now);
	m(r(now)) += m(now);
	v(l(now)) += m(now) * (mid - l + 1);
	v(r(now)) += m(now) * (r - mid);
	v(l(now)) %= p,m(l(now)) %= p;
	v(r(now)) %= p,m(r(now)) %= p;
	m(now) = 0;}
	ll build(ll l,ll r){
		ll now = ++cnt;
		m(now) = 0;
		if(l == r){
			v(now) = num[l];
			return cnt;
		}
		l(now) = build(l,mid);
		r(now) = build(mid + 1,r);
		up(now);
		return now;
	}
	void change(ll now,ll l,ll r,ll nl,ll nr,ll s){
		push(now,l,r);
		if(nl <= l && r <= nr){
			v(now) += (r - l + 1) * s;
			m(now) += s;
			v(now) %= p;
			m(now) %= p;
			return ;
		}
		if(nl <= mid)
			change(l(now),l,mid,nl,nr,s);
		if(nr > mid)
			change(r(now),mid + 1,r,nl,nr,s);
		up(now);
	}
	ll q(ll now,ll l,ll r,ll nl,ll nr){
		push(now,l,r);
		ll ans = 0;
		if(nl <= l && r <= nr)
		return v(now) % p;
		if(nl <= mid)
		ans = (ans + q(l(now),l,mid,nl,nr)) % p;
		if(nr > mid)
		ans = (ans + q(r(now),mid + 1,r,nl,nr)) % p;
		up(now);
		return ans;
	}
}Q;

int main(){
	scanf("%lld%lld%lld%lld",&n,&m,&root,&p);
	for(int i = 1;i <= n;++i){
		scanf("%lld",&v[i]);
	}
	for(int i = 1;i <= n - 1;++i){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(root,0);
	dfs2(root,root);
	cnt = 0;
	int z = Q.build(1,n);
//	for(int i = 1;i <= n;++i){
//		std::cout<<fa[i]<<" "<<dep[i]<<" "<<top[i]<<" "<<dfn[i]<<" "<<siz[i]<<" "<<gson[i]<<std::endl;
//	}
	for(int i = 1;i <= m;++i){
		ll opt,x,y,z;
		scanf("%lld",&opt);
		if(opt == 1){
			scanf("%lld%lld%lld",&x,&y,&z);
			z %= p;
			while(top[x] != top[y]){
				if(dep[top[x]] < dep[top[y]])std::swap(x,y);
				Q.change(1,1,n,dfn[top[x]],dfn[x],z);
				x = fa[top[x]];
			}
			if(dep[x] > dep[y]) std::swap(x,y);
			Q.change(1,1,n,dfn[x],dfn[y],z);
		}
		if(opt == 2){
			scanf("%lld%lld",&x,&y);
			ll ans = 0;
			while(top[x] != top[y]){
				if(dep[top[x]] < dep[top[y]])std::swap(x,y);
				ans = (ans + Q.q(1,1,n,dfn[top[x]],dfn[x])) % p;
				x = fa[top[x]];
			}
			if(dep[x] > dep[y])
			std::swap(x,y);
			ans = (ans + Q.q(1,1,n,dfn[x],dfn[y])) % p;
			std::cout<<ans % p<<std::endl;
		}
		if(opt == 3){
			scanf("%lld%lld",&x,&z);
			Q.change(1,1,n,dfn[x],dfn[x] + siz[x] - 1,z);
		}
		if(opt == 4){
			scanf("%lld",&x);
			std::cout<<Q.q(1,1,n,dfn[x],dfn[x] + siz[x] - 1) % p<<std::endl;
		}
	}
}//一个普通的维护路径权和的代码,还是写的不够快
#include<iostream>
#include<cstdio>
#define ll long long

struct P{
	ll to,next;
}e[500005 << 1];

ll n,m,root,p;
ll head[500005];
ll cnt = 0;

void add(ll x,ll y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

int dep[500005],fa[500005],siz[500005],gson[500005];

int dfncnt,dfn[500005],top[500005];

void dfs2(int u,int t){
	top[u] = t;
	dfn[u] = ++dfncnt;
	if(!gson[u])return;
	dfs2(gson[u],t);
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa[u] || v == gson[u])
		continue;
		dfs2(v,v);
	}
}

void dfs(ll u,ll f){
	fa[u] = f;
	dep[u] = dep[f] + 1;
	siz[u] = 1;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == f)
		continue;
		dfs(v,u);
		siz[u] += siz[v];
		if(siz[v] > siz[gson[u]])
		gson[u] = v;
	}
}

int main(){
	scanf("%lld%lld%lld",&n,&m,&root);
	for(int i = 1;i <= n - 1;++i){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(root,0);
	dfs2(root,root);
	for(int i = 1;i <= m;++i){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		while(top[x] != top[y]){
			if(dep[top[x]] < dep[top[y]]) std::swap(x,y);
			x= fa[top[x]];
		}
		if(dep[x] > dep[y])std::swap(x,y);
		std::cout<<x<<std::endl;
	}
}//树剖求LCA,我可能写丑了,并不很快

同时,需要知道的是,树剖是可以支持换根操作的,考虑换根之后对各种操作的影响即可
CF916E

#include <cstdio>
#include <algorithm>
#define lson rt<<1
#define rson rt<<1|1

const int N=1e5+5,M=2e5+5,logN=17+1;
int n,m,root,idx,a[N],f[N][logN],dfn[N],seq[N],sz[N],dep[N];
int tot,lnk[N],ter[M],nxt[M];
long long seg[N<<2],tag[N<<2];

void pushup(int rt) {
    seg[rt]=seg[lson]+seg[rson];
}
void build(int rt,int l,int r) {
    if(l==r) {
        seg[rt]=a[seq[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    pushup(rt);
}
void update(int rt,int l,int r,long long k) {
    seg[rt]+=1LL*(r-l+1)*k;
    tag[rt]+=k;
}
void pushdown(int rt,int l,int r) {
    if(!tag[rt]) return;
    int mid=(l+r)>>1;
    update(lson,l,mid,tag[rt]);
    update(rson,mid+1,r,tag[rt]);
    tag[rt]=0;
}
void modify(int x,int y,int rt,int l,int r,int k) {
    if(x<=l&&r<=y) {
        update(rt,l,r,k);
        return;
    }
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    if(x<=mid) modify(x,y,lson,l,mid,k);
    if(mid<y) modify(x,y,rson,mid+1,r,k);
    pushup(rt);
}
long long query(int x,int y,int rt,int l,int r) {
    if(x<=l&&r<=y) return seg[rt];
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    long long ret=0;
    if(x<=mid) ret+=query(x,y,lson,l,mid);
    if(mid<y) ret+=query(x,y,rson,mid+1,r);
    return ret;
}
void add(int u,int v) {
    ter[++tot]=v,nxt[tot]=lnk[u],lnk[u]=tot;
}
void dfs(int u,int fa) {
    dep[u]=dep[fa]+1,f[u][0]=fa,dfn[u]=++idx,seq[idx]=u,sz[u]=1;
    for(int i=1;(1<<i)<=dep[u];++i) f[u][i]=f[f[u][i-1]][i-1];
    for(int i=lnk[u];i;i=nxt[i]) {
        int v=ter[i];
        if(v==fa) continue;
        dfs(v,u),sz[u]+=sz[v];
    }
}
int lca(int u,int v) {
    if(dep[u]>dep[v]) u^=v^=u^=v;
    for(int i=17;~i;--i) if(dep[f[v][i]]>=dep[u]) v=f[v][i];
    if(u==v) return u;
    for(int i=17;~i;--i) if(f[u][i]^f[v][i]) u=f[u][i],v=f[v][i];
    return f[u][0];
}
int getlca(int u,int v,int p) {
    int x=lca(u,v),y=lca(u,p),z=lca(v,p);
    if(dep[y]>dep[x]) x=y;
    if(dep[z]>dep[x]) x=z;
    return x;
}
int jump(int u,int d) {
    for(int i=17;~i;--i) if(d&(1<<i)) u=f[u][i];
    return u;
}
void treeModify(int u,int k) {
    int l=dfn[u],r=dfn[u]+sz[u]-1;
    if(u==root) modify(1,n,1,1,n,k);
    else if(dfn[root]<l||dfn[root]>r) modify(l,r,1,1,n,k);
    else {
        int son=jump(root,dep[root]-dep[u]-1);
        modify(1,n,1,1,n,k),modify(dfn[son],dfn[son]+sz[son]-1,1,1,n,-k);
    }
}
long long treeQuery(int u) {
    int l=dfn[u],r=dfn[u]+sz[u]-1;
    if(u==root) return query(1,n,1,1,n);
    else if(dfn[root]<l||dfn[root]>r) return query(l,r,1,1,n);
    else {
        int son=jump(root,dep[root]-dep[u]-1);
        return query(1,n,1,1,n)-query(dfn[son],dfn[son]+sz[son]-1,1,1,n);
    }
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    for(int i=1;i<n;++i) {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs(1,0);
    build(1,1,n);
    root=1;
    while(m--) {
        int opt;
        scanf("%d",&opt);
        if(opt==1) {
            scanf("%d",&root);
        } else if(opt==2) {
            int u,v,x;
            scanf("%d%d%d",&u,&v,&x);
            treeModify(getlca(u,v,root),x);
        } else {
            int x;
            scanf("%d",&x);
            printf("%lld\n",treeQuery(x));
        }
    }
    return 0;
}

虚树

原本有很麻烦的做法的,但因为\(pbpb\)鸽鸽的一个做法,一切变得简单起来。

寻宝游戏

[SDOI2015]寻宝游戏
假设关键点按照 \(DFS\) 序排序后是\({a1,a2,…,ak}\)
那么所有关键点形成的极小联通子树的边权和的两倍等于 \(dist(a1,a2)+dist(a2,a3)+⋯+dist(ak−1,ak)+dist(ak,a1)\)
然后\(set\)维护一手,\(lca\)什么的树剖就好了

#include<iostream>
#include<cstdio>
#include<set>
#define ll long long

ll n,q;

struct P{
	ll to,next,v;
}e[200005];

ll cnt,head[200005];

void add(ll x,ll y,ll v){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	e[cnt].v = v;
	head[x] = cnt;
}

ll dcnt,fa[200005],idf[200005],dep[200005],siz[200005],son[200005],top[200005],dfn[200005],s[200005];

void dfs1(int now,int f){
	fa[now] = f;
	dep[now] = dep[f] + 1;
	siz[now] = 1;
	for(int i = head[now];i;i = e[i].next){
		int v = e[i].to;
		if(v == f)
		continue;
		s[v] = s[now] + e[i].v;
		dfs1(v,now);
		siz[now] += siz[v];
		if(siz[v] > siz[son[now]])
		son[now] = v;
	}
}

void dfs2(int now,int t){
	dfn[now] = dcnt ++ ;
	top[now] = t;
	idf[dfn[now]] = now;
	if(!son[now])
	return;
	dfs2(son[now],t);
	for(int i = head[now];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa[now] || v == son[now])
		continue;
		dfs2(v,v);
	}
}

std::set<int>st;
std::set<int>::iterator it;

ll ans = 0;

ll lca(ll x,ll y){
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]])std::swap(x,y);
		x = fa[top[x]];
	}
	if(dep[x] > dep[y])std::swap(x,y);
	return x;
}

ll dist(ll x,ll y){return s[x] + s[y] - 2 * s[lca(x,y)];}

bool vis[200005];

int main(){
	scanf("%lld%lld",&n,&q);
	for(int i = 1;i < n;++i){
		ll x,y,v;
		scanf("%lld%lld%lld",&x,&y,&v);
		add(x,y,v);
		add(y,x,v);
	}
	dfs1(1,0);
	dfs2(1,1);
	for(int i = 1;i <= q;++i){
		ll x;
		scanf("%lld",&x);
		x = dfn[x];
		if(!vis[idf[x]])st.insert(x);
		ll y = idf[(it = st.lower_bound(x)) == st.begin() ? * -- st.end() : * --it];
		ll z = idf[(it = st.upper_bound(x)) == st.end() ? *st.begin() : * it];
		if(vis[idf[x]])st.erase(x);
		x = idf[x];
		ll d = dist(x,y) + dist(x,z) - dist(y,z);
		if(!vis[x])vis[x] = 1,ans += d;
		else
		vis[x] = 0,ans -= d;
		std::cout<<ans<<std::endl;
	}
	return 0;
}

[SDOI2011]消耗战

[SDOI2011]消耗战
板子题,代码明天给补上。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define M 1000005
#define N 500005

struct P{
	ll to,next,v;
}e[M],e2[M];

ll cnt,head[N],dfn[N],idf[N],top[N],Minn[N],dep[N],fa[N],siz[N],son[N];
ll dcnt;
ll cnt2,head2[N],DP[N];

void add(ll x,ll y,ll v){
	e[++cnt].to = y;
	e[cnt].v = v;
	e[cnt].next = head[x];
	head[x] = cnt;
}

void add2(ll x,ll y){
	e2[++cnt2].to = y;
	e2[cnt2].next = head2[x];
	head2[x] = cnt2;
}

void dfs1(ll now,ll f){
	fa[now] = f;
	dep[now] = dep[f] + 1;
	siz[now] = 1; 
	for(int i = head[now];i;i = e[i].next){
		ll v = e[i].to;
		if(v != f){
		Minn[v] = std::min(Minn[now],e[i].v);
		dfs1(v,now);
		siz[now] += siz[v];
		if(siz[v] > siz[son[now]])
		son[now] = v;
		}
	}
}

void dfs2(ll now,ll t){
	top[now] = t;
	dfn[now] = ++dcnt;
	idf[dfn[now]] = now;
	if(!son[now])
	return;
	dfs2(son[now],t);
	for(int i = head[now];i;i = e[i].next){
		ll v = e[i].to;
		if(v == fa[now] || v == son[now])
		continue;
		dfs2(v,v);
	}
}

ll lca(ll x,ll y){
	while(top[x] != top[y]){
		if(dep[top[x]] < dep[top[y]])
		std::swap(x,y);
		x = fa[top[x]];
	}
	if(dep[x] > dep[y])
	std::swap(x,y);
	return x;
}

ll stack[N];

void build(){
	cnt2 = 0;
	std::sort(stack + 1,stack + stack[0] + 1);
	ll s = stack[0];
	for(int i = 2;i <= s;++i)
	stack[++stack[0]] = dfn[lca(idf[stack[i]],idf[stack[i - 1]])];
	std::sort(stack + 1,stack + stack[0] + 1);
	s = std::unique(stack + 1,stack + stack[0] + 1) - stack - 1;
	for(int i = 2;i <= s;++i)
	add2(lca(idf[stack[i]],idf[stack[i - 1]]),idf[stack[i]]);
}

bool qr[N];

void dfs(ll now){
	ll s = 0;
	DP[now] = Minn[now];
	for(int i = head2[now];i;i = e2[i].next){
		ll v = e2[i].to;
		dfs(v);
		s += DP[v];
	}
	if(s && !qr[now])
	DP[now] = std::min(s,DP[now]);
	else
	qr[now] = 0;
	head2[now] = 0;
}

ll n,q;

int main(){
	scanf("%lld",&n);
	std::memset(Minn,0x7f7f7f7f,sizeof(Minn));
	for(int i = 1;i <= n - 1;++i){
		ll x,y,v;
		scanf("%lld%lld%lld",&x,&y,&v);
		add(x,y,v);
		add(y,x,v);
	}
	dfs1(1,0);
	dfs2(1,1);
	scanf("%lld",&q);
	while(q -- ){
		stack[0] = 0;
		ll x;
		scanf("%lld",&x);
		for(int i = 1;i <= x;++i){
			ll s;
			scanf("%lld",&s);
			qr[s] = 1;
			stack[++stack[0]] = dfn[s];
		}
		stack[++stack[0]] = dfn[1];
		build();
		dfs(1);
		std::cout<<DP[1]<<std::endl;
	}
}

淀粉质

正如\(fdy\)老师讲的一样,淀粉质实际上不过是建出重心重构树,然后分治罢了

Tree

P4178 Tree

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define N 40005
#define M 80005

struct P{
	int to,next,v;
}e[M];

int cnt,head[N],dis[N],siz[N],dp[N],rev[N];
bool vis[N];

void add(int x,int y,int v){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	e[cnt].v = v;
	head[x] = cnt;
}

ll n,k,rt,sum;

void getrt(ll u,ll fa){
	siz[u] = 1,dp[u] = 0;
	for(int i = head[u];i;i = e[i].next){
		ll v = e[i].to;
		if(v == fa || vis[v])
		continue;
		getrt(v,u);
		siz[u] += siz[v];
		dp[u] = std::max(dp[u],siz[v]);
	}
	dp[u] = std::max((ll)dp[u],sum - siz[u]);
	if(dp[u] < dp[rt])
	rt = u;
}

ll ans = 0;
ll tot = 0;

void getdis(ll u,ll fa){
	rev[++tot] = dis[u];
	for(int i = head[u];i;i = e[i].next){
		ll v = e[i].to;
		if(v == fa || vis[v])
		continue;
		dis[v] = dis[u] + e[i].v;
		getdis(v,u);
	}
}

ll doit(ll u,ll s){
	tot = 0,dis[u] = s,getdis(u,0);
	std::sort(rev + 1,rev + tot + 1);
	ll l = 1,r = tot;
	ll ans = 0;
	while(l <= r)
	if(rev[l] + rev[r] <= k)
	ans += r - l,++l;
	else
	--r;
	return ans;
}

void solve(ll u){
	vis[u] = 1,ans += doit(u,0);
	for(int i = head[u];i;i = e[i].next){
		ll v = e[i].to;
		if(vis[v])continue;
		ans -= doit(v,e[i].v);
		sum = siz[v],dp[0] = n,rt = 0;
		getrt(v,u),solve(rt);
	}
}

int main(){
	scanf("%lld",&n);
	for(int i = 1;i < n;++i){
		ll x,y,v;
		scanf("%lld%lld%lld",&x,&y,&v);
		add(x,y,v);
		add(y,x,v);
	}
	scanf("%lld",&k);
	dp[0] = sum = n,getrt(1,0),solve(rt);
	std::cout<<ans<<std::endl;
}

[国家集训队]聪聪可可

[国家集训队]聪聪可可
!!!记得求重心的时候\(dp[u]\)要清$0!!

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 20005
#define M 40005

struct P{
	int to,v,next;
}e[M];

int cnt,head[N];

void add(int x,int y,int v){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	e[cnt].v = v;
	head[x] = cnt;
}

ll sum,rt,siz[N],dp[N];

bool vis[N];

void get_rt(int u,int fa){
	siz[u] = 1,dp[u] = 0;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa || vis[v])
		continue;
		get_rt(v,u);
		siz[u] += siz[v];
		dp[u] = std::max(siz[v],dp[u]); 
	}
	dp[u] = std::max(sum - siz[u],dp[u]);
	if(dp[u] < dp[rt])
	rt = u;
}

ll dcnt[4],dis[N];
ll tot = 0;

void get_dis(int u,int fa){
	dcnt[dis[u] % 3] ++ ;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa || vis[v])
		continue;
		dis[v] = dis[u] + e[i].v;
		get_dis(v,u);
	}
}

ll ans = 0;

ll doit(int u,int s){
	dcnt[0] = dcnt[1] = dcnt[2] = 0;
	dis[u] = s;
	get_dis(u,0);
	ll ans = 0;
	ans += dcnt[0] * dcnt[0];
	ans += dcnt[1] * dcnt[2] * 2; 
 	return ans;
}

ll n;

void solve(int u){
	vis[u] = 1,ans += doit(u,0);
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(vis[v])continue;
		ans -= doit(v,e[i].v);
		sum = siz[v],dp[0] = n,rt = 0;
		get_rt(v,u),solve(rt);
	}
}

ll gcd(ll a,ll b){return (!b) ? a : gcd(b,a % b);}

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n - 1;++i){
		ll x,y,v;
		scanf("%lld%lld%lld",&x,&y,&v);
		add(x,y,v);
		add(y,x,v);
	}
	sum = n,dp[0] = n,rt = 0;
	get_rt(1,0),solve(rt);
	ll g = gcd(ans,n * n);
	printf("%lld/%lld",(ans / g),(n * n / g));
}

树上启发式合并

考虑暴力进行一些操作,这样我们发现,对于每颗子树,我们做完这颗子树后,由于不同子树之间不能互相影响,所以需要进行一个清空操作,我们可以通过调换顺序,来保证复杂度,考虑把重儿子的子树放在最后进行操作,这样这颗子树的答案不用清空可以和答案直接合并。

CF600E Lomsat gelral

CF600E Lomsat gelral
(注意遍历子树是\(e[i].next\)不是\(e[i].to\),写错很有可能通过输出无法查出错误,这种情况不如检查一下这里)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
#define N 100005
#define M 200005

struct P{
	ll to,next;
}e[M];

int cnt,C[N],head[N],c[N],siz[N],gson[N];
ll ans[N],fans[N];

void add(int x,int y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}

ll n;

void dfs(int u,int fa){
	siz[u] = 1;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa)
		continue;
		dfs(v,u);
		siz[u] += siz[v];
		if(siz[v] > siz[gson[u]])
		gson[u] = v;
	}
}

std::queue<int>QWQ;

ll maxx;

void clear(){
	maxx = 0;
	while(!QWQ.empty()){
		C[QWQ.front()] = 0;
		QWQ.pop();
	}
}

ll t;

void init(int u,int fa){
	QWQ.push(c[u]);
	C[c[u]] ++ ;
	if(C[c[u]] > maxx)
	fans[t] = c[u],maxx = C[c[u]];
	else
	if(C[c[u]] == maxx)
	fans[t] += c[u];
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa || v == gson[t])
		continue;
		init(v,u);
	}
}

void solve(int u,int fa){
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(v == fa || v == gson[u])
		continue;
		solve(v,u);
		clear();
	}
	if(gson[u])
	solve(gson[u],u);
	t = u;
	fans[u] = fans[gson[u]];
	init(u,fa);
}

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i)
	scanf("%d",&c[i]);
	for(int i = 1;i < n;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	solve(1,0);
	for(int i = 1;i <= n;++i)
	printf("%lld ",fans[i]);
}//该开ll就开ll,不开ll这题就被卡了
原文地址:https://www.cnblogs.com/dixiao/p/14520857.html