• 【树上问题杂谈】


    简介

    好,我摸爬滚打了好几天,干掉了数论
    接下来找树了(
    可能主要是一些树上维护信息,树形\(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这题就被卡了
    
  • 相关阅读:
    zz 使用svn——项目的目录布局
    eclipse中字体太小
    SVN 项目的目录结构
    tuscany requires 学习
    搜索子集生成
    HZNUACM寒假集训Day12小结 数论入门
    HZNUACM寒假集训Day10小结 树树形DP
    模板 快速幂|取余
    HZNUACM寒假集训Day7小结 背包DP
    HZNUACM寒假集训Day6小结 线性DP
  • 原文地址:https://www.cnblogs.com/dixiao/p/14520857.html
Copyright © 2020-2023  润新知