题意:
n个点的树,root为1,每条边有权
- 定义一个结点的val==此结点为根的子树中所有其他结点到此结点的距离和
- 定义两个结点(一个是另一个的祖宗)的sub==val差
求sub在小于等于m范围内的最大值
解析:
首先要在N时间内预处理所有点的val:
对于点1,有儿子2和3,val[2]和val[3]已知,且子树2和3的结点数已知,那么
val[1]==val[2]+val[3]+dis[1][2]*size(2)+dis[1][3]*size(3)
,就是在得出2和3的基础上,加上所有1的子孙的“2或3到1的那段”接下来是处理sub
因为两个sub的点在一条轴上,所以可以类似于树的中序遍历做,用一个栈存数据,遍历时,往下就入栈,往上就出栈,由于这道题父结点val一定比较大,所以这个栈单调减
在遍历到一个点i的时候,在前面找到第一个小于等于val[i]+m的点,在找得到的情况下,这个点就是点i所能配对最适合ans的祖宗
代码:
#include<bits/stdc++.h>
using namespace std;
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define D long long
#define pill pair<D,D>
#define mk make_pair
#define N 100009
D read(){
D ans=0;char last=' ',ch=getchar();
while(!isdigit(ch))last=ch,ch=getchar();
while(isdigit(ch))ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans;
return ans;
}
D n,m,t;
vector<pill>v[N];
D siz[N],val[N];
void getval(D p,D f){
siz[p]=1;
val[p]=0;
for(int i=0;i<v[p].size();i++){
D a=v[p][i].first,b=v[p][i].second;
if(a==f)continue;
getval(a,p);
val[p]+=val[a];
siz[p]+=siz[a];
val[p]+=siz[a]*b;
}
}
D sta[N];int top;
D ans;
bool cmp(D a,D b){
return a>b;
}
void getans(D p,D f){
sta[++top]=val[p];
int pos=lower_bound(sta+1,sta+1+top,val[p]+m,cmp)-sta;
if(pos!=top)ans=max(ans,sta[pos]-val[p]);
for(int i=0;i<v[p].size();i++){
D a=v[p][i].first;
if(a==f)continue;
getans(a,p);
}
top--;
}
int main(){
t=read();while(t--){
n=read(),m=read();
for(int i=1;i<=n;i++)v[i].clear();
for(int i=1;i<n;i++){
D a=read(),b=read(),vv=read();
v[a].pb(mk(b,vv)),v[b].pb(mk(a,vv));
}
getval(1,-1);
top=0;ans=-1;
getans(1,-1);
printf("%lld\n",ans);
}
}