1 条题解

  • 0
    @ 2026-9-3 16:06:46

    Problem Link

    题目大意

    给定 nn 个点的树,每个点有点权 wiw_iqq 次询问 L,RL,R,构造一组 cic_i 使得每个子树内 cic_i 的和都在 [0,1][0,1] 之间,最小化 ciwi\sum|c_i|w_i

    数据范围;n2×105,q105n\le 2\times 10^5,q\le 10^5

    思路分析

    先从 wi=1w_i=1 的情况开始分析,此时在哪里填 cc 没有区别,只关心总和。

    自下而上地开始填 cic_i,首先在每个叶子上都有 ci=Lc_i=L,在此之后,每个点的子树和都 L\ge L,我们只要给一些非叶子结点的 cc 设为负数以保证其总和 R\le R,容易证明减到 <L<L 是不优的。

    观察根节点处的变化量,设原有 kk 个叶子,那么要让根节点合法,整棵树的变化量至少为 max(0,kLR)\max(0,kL-R)

    不难证明这个界是可以取到的,可以每个 >R>R 的节点处减到 RR,可以证明这样不会有冗余操作。

    然后考虑 wi{0,1}w_i\in\{0,1\} 的情况,容易发现此时我们能在 wi=0w_i=0 的点上任意操作,因此我们一定能在每个 wi=0w_i=0 的点上把子树和调整到 LL

    这相当于把每个 wi=0w_i=0 的点看成叶子,然后对每个连通块分别求解答案再求和。

    设有 kk 个叶子的连通块有 fkf_k 个,答案就是 kfk×max(0,kLR)\sum_k f_k\times \max(0,kL-R),求出第一个 kL>RkL>R 的位置维护 fkf_kk×fkk\times f_k 的后缀和即可快速计算答案。

    然后考虑一般的情况,由于我们已经会解决 wi{0,1}w_i\in\{0,1\} 的情况了,因此不妨猜测更一般的情况可以向这种情况规约。

    对每个 xx,将 wi>xw_i>x 的点看成 11wixw_i\le x 的点看成 00,然后对每个 xx 求出答案再相加,可以根据本题的直接贪心过程证明之。

    依然考虑维护 fk\sum f_k,设 w1wnw_1\sim w_n 是递增的,那么我们就要依次删除 1n1\sim n,删除 1i1\sim i 后的一个叶子数为 kk 的连通块对 fkf_k 的贡献就是 wi+1wiw_{i+1}-w_i

    首先我们肯定转成倒序插入节点,可以用并查集维护产生和删除的每个连通块。

    并且可以考虑差分,即一个连通块在插入 xx 时刻生成,就对 fkf_k 产生 +wx+w_x 贡献,在插入 yy 时刻消失,就对 fkf_k 产生 wy-w_y 贡献。

    那么这样就可以维护出所有 fkf_k 并计算答案,最终答案记得加上 LL 倍叶子权值的和。

    时间复杂度 O(nlogn+q)\mathcal O(n\log n+q)

    代码呈现

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int MAXN=2e5+5;
    vector <int> G[MAXN];
    int n,w[MAXN],fa[MAXN],dsu[MAXN],siz[MAXN];
    bool vis[MAXN];
    ll cnt[MAXN],s1[MAXN],s2[MAXN],clf;
    int find(int x) { return x^dsu[x]?dsu[x]=find(dsu[x]):x; }
    void add(int x,int c,int v) { cnt[siz[x]]-=v,cnt[siz[x]+=c]+=v; }
    void merge(int x,int y,int v) { dsu[y]=x,cnt[siz[y]]-=v,add(x,siz[y],v); }
    void ins(int x) {
    	vis[x]=true;
    	if(fa[x]&&vis[fa[x]]) add(find(fa[x]),-1,w[x]);
    	for(int y:G[x]) {
    		if(!vis[y]) add(x,1,w[x]);
    		else merge(x,y,w[x]);
    	}
    	if(vis[fa[x]]) merge(find(fa[x]),x,w[x]);
    }
    void init(vector <int> P,vector <int> W) {
    	n=W.size(); vector <int> ord;
    	for(int i=1;i<=n;++i) w[i]=W[i-1],ord.push_back(i),dsu[i]=i;
    	for(int i=2;i<=n;++i) G[fa[i]=P[i-1]+1].push_back(i);
    	for(int i=1;i<=n;++i) if(G[i].empty()) G[i].push_back(0),clf+=w[i];
    	sort(ord.begin(),ord.end(),[&](int x,int y){ return w[x]>w[y]; });
    	for(int u:ord) ins(u);
    	for(int i=n;i>=1;--i) s1[i]=s1[i+1]+cnt[i],s2[i]=s2[i+1]+cnt[i]*i;
    }
    ll query(int L,int R) {
    	int x=min(n,R/L)+1;
    	return clf*L+s2[x]*L-s1[x]*R;
    }
    
    • 1

    信息

    ID
    7395
    时间
    2000ms
    内存
    2048MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者