Description:
一棵树,有
q
q
q个询问,每次询问以
x
x
x为中心,建
y
y
y条河,满足河一定与
x
x
x连通,求每次询问的最大价值(河覆盖的路径长度)。
n
,
q
≤
1
0
5
n,q\le 10^5
n,q≤105
Solution:
- 首先,我们可以得到一个定理:
- 对于一个无根树,如果它有 2 k 2k 2k个叶子节点,我们只用 k k k条路径就足以将其覆盖。所以我们就可以把问题变为:在无根树中找 2 y 2y 2y个叶子节点,是其形成一个包含点x的边权最大连通块 S S S。
- 也就是说我们以 x x x为根节点,找 2 y 2y 2y个叶子节点,使它们到形成的 S S S最大。
- 而模拟发现,我们把这些叶子节点产生的贡献 v a l val val,选出最大的 2 y − 1 2y-1 2y−1个即为答案。
- 但是对于每个询问的 x x x都是不同的。
- 然后,我们可以发现对于每次询问, S S S中一定会至少包含此树直径中的两端点之一。
- 所以我们可以分别以直径两端点为根节点的树中找最大的 2 y − 1 2y−1 2y−1个叶子节点的 v a l val val之和。
- 但是这样就又有一个问题了,那就是这样写会导致 x x x可能不在 S S S之中。
- 但这并不碍事,我们可以将其中一条最小的删掉,来连向 x x x。
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
const int N=1e5+2,S=20;
int n,q;
int qwq,head[N];
struct edge{
int to,nxt;
int w;
}E[N<<1];
void addedge(int x,int y,int z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
#define EREP(x) for(int i=head[x];~i;i=E[i].nxt)
int Mx,Id;
void dfs(int x,int f,int dis){
if(chkmax(Mx,dis)) Id=x;
EREP(x){
int y=E[i].to;
if(y==f)continue;
dfs(y,x,dis+E[i].w);
}
}
int val_cmp[N];
bool cmp(int x,int y){return val_cmp[x]>val_cmp[y];}
struct node{
int rt;
int far[N],dis[N];
int fa[N][S];
int cnt,leaf[N],val[N],sum[N],id[N];
void dfs1(int x,int f){
fa[x][0]=f;
bool child=0;
EREP(x){
int y=E[i].to;
if(y==f)continue;
child=1;
dis[y]=dis[x]+E[i].w;
dfs1(y,x);
if(dis[far[x]]<dis[far[y]]) far[x]=far[y];
}
if(!child)leaf[++cnt]=x,far[x]=x;
}
void dfs2(int x,int v){
val[x]=v;
EREP(x){
int y=E[i].to;
if(y==fa[x][0])continue;
if(far[x]==far[y]) dfs2(y,v+E[i].w);
else dfs2(y,E[i].w);
}
}
void init(){
dfs1(rt,0);
SREP(j,1,S) REP(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
dfs2(rt,0);
memcpy(val_cmp,val,sizeof val_cmp);
sort(leaf+1,leaf+1+cnt,cmp);
REP(i,1,cnt) id[leaf[i]]=i,sum[i]=sum[i-1]+val[leaf[i]];
}
int solve(int x,int y){
if(y>=cnt) return sum[cnt];
if(y>=id[far[x]]) return sum[y];
int t=far[x];
DREP(i,S-1,0) if(fa[x][i] and id[far[fa[x][i]]]>y) x=fa[x][i];
x=fa[x][0];
return sum[y]+dis[t]-dis[x]-min(dis[far[x]]-dis[x],val[leaf[y]]);
}
}A,B;
void Init(){
Mx=0;
dfs(1,0,0);
A.rt=Id;
Mx=0;
dfs(Id,0,0);
B.rt=Id;
A.init();
B.init();
}
int main(){
// freopen("farm.in","r",stdin);
// freopen("farm.out","w",stdout);
scanf("%d%d",&n,&q);
memset(head,-1,sizeof head);
SREP(i,1,n){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c);
addedge(b,a,c);
}
Init();
while(q--){
int x,y;
scanf("%d%d",&x,&y);
y=y*2-1;
int ans=0;
chkmax(ans,max(A.solve(x,y),B.solve(x,y)));
printf("%d\n",ans);
}
return 0;
}