洛谷P3925 aaabx 题解

题意分析

给你一颗树,每个点有个权值 \(v_i\),对于每个点,把它子树从小到大排序乘以排名称为这个点的贡献,求每个点的贡献和对 \(10^9+7\) 取余。

题解

下面 \(sz_i\) 就是 \(i\) 的子树大小,\(fa_i\) 为它自己和它所有父亲节点的集合。

先想暴力,暴力就是把每个点子树的权值全部拿出来然后排序,按照上面题意排个序求个贡献在求个和就完了。

但是这样感觉没法优化,于是我们考虑每个点对这个子树的贡献,考虑一些特殊的点,比如说最大的点,最大的点 \(i\) 在它的子树及它所有上级的子树中的贡献就是 \(v_i \times \sum\limits_{j\in fa_i} sz_j\)

然后我们再考虑次大值,次大值在有刚刚有最大值的子树 \(i\) 中所成排名就会变成 \(sz_i-1\),这样下去我们就得到了一个规律,就是每次处理当前最大值时就把它自己和它所有父亲的 \(sz\) 减一,让后每次都这么做就成了两个操作:

  1. 求当前点到父亲的 \(sz\) 和。

  2. 将当前点到父亲的 \(sz\) 减一。

这样就成了一个树链剖分的板子,最终时间复杂度 \(o(n\log^2 n)\)

代码

#include <bits/stdc++.h>
#define ll long long
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define mkp make_pair
#define pll pair<ll, ll>
char *SSSS, *TTTT;
char pori[1 << 22];
#define gc() (SSSS == TTTT && (TTTT = (SSSS = pori) + fread(pori, 1, 1 << 22, stdin)), SSSS == TTTT ? EOF : *SSSS++)
inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * f;
}
using namespace std;
const ll N=5e5+5,mod=1e9+7;
ll n,m,rt;
ll w[N],nw[N];
ll dep[N],sz[N],top[N],f[N],son[N],id[N],cnt;
vector<int> g[N];
struct node{
	ll l,r;
	ll lzy,tot;
}t[N<<2];
inline void dfs(ll u,ll fa,ll dp){
	dep[u]=dp;
	f[u]=fa;
	sz[u]=1;
	for (const int &v:g[u]){
		if(v!=fa){
			dfs(v,u,dp+1);
			sz[u]+=sz[v];
			if(sz[son[u]]<sz[v]){
				son[u]=v;
			}
		}
	}
	w[u]=sz[u];
}
inline void dfs2(ll u,ll tp){
	id[u]=++cnt;
	nw[cnt]=w[u];
	top[u]=tp;
	if(!son[u]){
		return ;
	}
	dfs2(son[u],tp);
	for (const int &v:g[u]){
		if(v==f[u]||v==son[u]){
			continue;
		}
		dfs2(v,v);
	}
}
#define lc(p) (p<<1)
#define rc(p) (p<<1|1)
inline void pushup(ll p){
	t[p].tot=(t[lc(p)].tot+t[rc(p)].tot)%mod;
}
inline void pushdown(ll p){
	node &rt = t[p], &l = t[lc(p)], &r = t[rc(p)];
    if (rt.lzy)
    {
        l.lzy += rt.lzy, l.tot += rt.lzy * (l.r - l.l + 1)%mod;
        l.lzy %= mod, l.tot %= mod;
        r.lzy += rt.lzy, r.tot += rt.lzy * (r.r - r.l + 1)%mod;
        r.lzy %= mod, r.tot %= mod;
        rt.lzy = 0;
    }
}
inline void build(ll p,ll l,ll r){
	t[p]=node{l,r,0,0};
	if(l==r){
		t[p].tot=nw[l];
		return ;
	}
	ll mid=(l+r)>>1;
	build(lc(p),l,mid);
	build(rc(p),mid+1,r);
	pushup(p);
}
inline void add(ll p,ll ql,ll qr,ll d){
	if(ql<=t[p].l&&t[p].r<=qr){
		t[p].lzy+=d;
		t[p].lzy%=mod;
		t[p].tot+=(t[p].r-t[p].l+1)*d%mod;
		t[p].tot%=mod;
		return ;
	}
	pushdown(p);
	ll mid=(t[p].l+t[p].r)>>1;
	if(ql<=mid){
		add(lc(p),ql,qr,d);
	}
	if(mid<qr){
		add(rc(p),ql,qr,d);
	}
	pushup(p);
}
inline ll query(ll p,ll ql,ll qr){
	if(ql<=t[p].l&&t[p].r<=qr){
		return t[p].tot;
	}
	pushdown(p);
	ll mid=(t[p].l+t[p].r)>>1;
	ll ret=0;
	if(ql<=mid){
		ret=(ret+query(lc(p),ql,qr))%mod;
	}
	if(mid<qr){
		ret=(ret+query(rc(p),ql,qr))%mod;
	}
	return ret;
}
inline void updpath(ll u, ll v, ll d)
{
    while (top[u] != top[v])
    {
        if (dep[top[u]] < dep[top[v]])
        {
            swap(u, v);
        }
        add(1, id[top[u]], id[u], d);
        u = f[top[u]];
    }
    if (dep[u] < dep[v])
    {
        swap(u, v);
    }
	add(1, id[v], id[u], d);
}
inline ll querypath(ll u, ll v)
{
    ll ret = 0;
    while (top[u] != top[v])
    {
        if (dep[top[u]] < dep[top[v]])
        {
            swap(u, v);
        }
        ret += query(1, id[top[u]], id[u]);
        ret%=mod;
        u = f[top[u]];
    }
    if (dep[u] < dep[v])
    {
        swap(u, v);
    }
    ret += query(1, id[v], id[u]);
    ret%=mod;
    return ret;
}
pll a[N];
int main(){
	n=read();
	rep(i,2,n){
		ll u=read(),v=read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	rt=1;
	dfs(rt,-1,1);
	dfs2(rt,rt);
	build(1,1,n);
	rep(i,1,n){
		a[i]=mkp(read(),i);
	}
	sort(a+1,a+n+1,greater<pll>());
	ll ans=0;
	rep(i,1,n){
		ll x=a[i].first,y=a[i].second;
		ans=(ans+x*querypath(1,y)%mod)%mod;
		updpath(1,y,-1);
	}
	printf("%lld\n",ans);
	return 0;
} 
posted @ 2025-06-17 19:49  点燃genshin  阅读(17)  评论(0)    收藏  举报