题意
有一棵 n n n个节点的树,每个点有一个初始权值 w i w_i wi,要求支持两种操作:
1、使 x x x的点权增加 y y y
2、给出 k k k,选定 k k k个点使得包含这 k k k个点的最小联通子图点权和最大,你只需要输出这个最大值。
n , q ≤ 1 0 5 n,q\le10^5 n,q≤105
分析
若 k = 2 k=2 k=2,显然选的是带权最长链。
可以发现 k k k每增加 1 1 1,新的答案显然是在原来最优方案的基础上,新加入一个叶节点。
暴力的做法是先找到带权最长链,使链上点的点权变为 0 0 0,然后每次找从直径端点开始的最长链,再把最长链上点权变为 0 0 0.
可以发现这个过程实际上就是从带权直径的一个端点为根作长链剖分,然后选出前 k k k大的链作为答案。
可以用lct来维护长链剖分,每次更新点权后,不断往上更新长链,相当于lct的access操作。
由于必须要以带权直径的端点作为根,所以修改点权后可能要进行换根操作。具体实现的时候可以在access到直径的时候,选出对应点较大的一端包含的端点换为根,然后正常维护即可。
还要用数据结构来记录每一条链的权值。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
typedef long long LL;
const int N=100005;
const LL inf=(LL)1e14;
int n,q,rt,cnt,last[N];
LL dis[N],w[N],rtd;
struct tree{
int l,r,s,fa;LL w;bool rev;}t[N];
struct edge{
int to,next;}e[N*2];
std::multiset<LL> se;
std::multiset<LL>::iterator it;
struct Segtree
{
int rt,sz;
struct tree{
int l,r,s;LL w;}t[N*80];
void ins(int &d,LL l,LL r,LL x,int y)
{
if (!d) d=++sz;
t[d].s+=y;t[d].w+=x*y;
if (l==r) return