这道题听说是一道点分的题,但是我不太想写,而且确实不太会
所以呢就随手写了个类似树规的东西
先考虑一个节点的左右节点相互之间的贡献
为了防止转移次数过多 我们把递归的下一层的重儿子的set拿来直接用
只要打上一个所连边权值的标记,就可以用来给这一层用了
而对于其他节点的set里的元素,我们直接枚举放到这里面就可以了
然后每加入一个元素 就用lower_bound查询第一个和大于S的值
查看是否符合要求并与现在的答案比较一下 就可以处理完了
而对于一直往上的链的贡献 只要dfs一遍处理前缀边权和
然后用倍增找每个节点第一个符合要求的值更新答案就可以了
付大度大概介于nlogn到nlog2之间吧,有O2就跑得很快了
贴上代码
#include
using namespace std;
set tong[100005];
int root[100005],size[100005],head[100005],nxt[200005],to[200005];
int son[100005],flag[100005],edge[100005],w[200005],tot,depth[200005];
int s,ans=0x7fffffff,n,e,shu1,shu2,shu3,all,father[100005][21];
int minn(int x,int y)
{
if(xsize[son[x]]) son[x]=y,edge[x]=i;
}
}
void dp(int x,int fa)
{
if(son[x])
{
dp(son[x],x);
root[x]=root[son[x]];
flag[x]=flag[son[x]]+w[edge[x]];
tong[root[x]].insert(-flag[x]);
}
else
{
root[x]=++all;
tong[root[x]].insert(0);
}
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa||y==son[x]) continue;
dp(y,x);
flag[y]+=w[i];
for(set::iterator it=tong[root[y]].begin(),ed=tong[root[y]].end();it!=ed;it++)
{
set::iterator another=tong[root[x]].lower_bound(s-*it-flag[y]-flag[x]);
int now=*another+*it+flag[x]+flag[y];
if(now>=s&&now<=e)
ans=minn(ans,now);
}
for(set::iterator it=tong[root[y]].begin(),ed=tong[root[y]].end();it!=ed;it++)
{
tong[root[x]].insert(*it+flag[y]-flag[x]);
}
}
}
void go(int x,int fa)
{
for(int i=1;i<=20;i++)
father[x][i]=father[father[x][i-1]][i-1];
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
depth[y]=depth[x]+w[i];
father[y][0]=x;
go(y,x);
}
}
void deal(int x)
{
int now=0;
for(int i=20;i>=0;i--)
{
if(now+depth[x]-depth[father[x][i]]=s&&now<=e) ans=minn(ans,now);
}
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
cin>>n>>s>>e;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&shu1,&shu2,&shu3);
add(shu1,shu2,shu3);
add(shu2,shu1,shu3);
}
dfs(1,1);
dp(1,1);
go(1,1);
for(int i=1;i<=n;i++)
{
deal(i);
}
if(ans==0x7fffffff)
{
cout<<-1;
}
else cout<