【NOIP2016提高A组模拟8.19】(雅礼联考day2)树上路径
题目
给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)。
分析
点分治,设当前为x的,求在以x为根的子树中,经过x的路径(包括起点或终点在x)中长度大于等于S的最小值。
假设i有3个儿子,j、k、l,
首先将以j为根的子树中的所有点到x的距离求出来,放进队列中。排个序。
接着将以k为根的子树中的所有点到x的距离求出来,一个一个点枚举,在队列中二分,求出一段大于等于S并且最小的路径,与ans比较,取小。再将它们放进队列中。排个序。
再以j为根的子树中的所有点到x的距离求出来,同样更新答案,在再加入队列。
对于起点或终点在x的,在一开始就加入队列,就可以了。
时间复杂度\(O(nlog_2^2n)\)
事实上菊花图卡不过,但还是水过了。
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
const int mo=1000000007;
const int N=100005;
using namespace std;
int dis[N],d[N],root,last[N],next[N*2],to[N*2],v[N*2],s,e,ans=maxlongint,n,m,tot,size[N],mx,ff;
bool bz[N];
int bj(int x,int y,int z)
{
next[++tot]=last[x];
last[x]=tot;
to[tot]=y;
v[tot]=z;
}
int findroot(int x,int fa)
{
size[x]=1;
int num=0;
for(int i=last[x];i;i=next[i])
{
int j=to[i];
if(j!=fa && bz[j])
{
findroot(j,x);
size[x]+=size[j];
num=max(num,size[j]);
}
}
num=max(ff-size[x],num);
if(num<mx)
{
root=x;
mx=num;
}
}
int sodis(int x,int fa,int val)
{
d[++tot]=val;
for(int i=last[x];i;i=next[i])
{
int j=to[i];
if(j!=fa && bz[j])
{
sodis(j,x,val+v[i]);
}
}
}
int rf(int l,int r,int val)
{
while(l<r)
{
int mid=(l+r)/2;
if(d[mid]+val<s)
l=mid+1;
else
r=mid;
}
if(d[l]+val>=s)
ans=min(d[l]+val,ans);
else
if(d[r]+val>=s)
ans=min(d[r]+val,ans);
}
int dg(int x,int fa)
{
bz[x]=false;
tot=1;
d[1]=0;
for(int i=last[x];i;i=next[i])
{
int j=to[i];
if(j!=fa && bz[j])
{
int k=tot+1;
sodis(j,x,v[i]);
for(int l=k;l<=tot;l++)
{
rf(1,k-1,d[l]);
}
sort(d+1,d+1+tot);
}
}
int f=tot;
for(int i=last[x];i;i=next[i])
{
int j=to[i];
if(j!=fa && bz[j])
{
ff=f-1;
root=0;
mx=maxlongint;
findroot(j,x);
dg(root,x);
}
}
}
int main()
{
scanf("%d%d%d",&n,&s,&e);
for(int i=1;i<=n-1;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
bj(x,y,z);
bj(y,x,z);
}
memset(bz,true,sizeof(bz));
root=0;
ff=n;
mx=maxlongint;
findroot(1,0);
dg(root,0);
if(ans>e)
printf("-1\n");
else
printf("%d\n",ans);
}