【题目描述】
Description
Tar国正在准备每年一次的巡游活动。国王将会在一个城市S里召集人群,沿着城市间的道路进行游览,最终在一个城市T里发表他每年一次的著名演讲。
Tar国有N个城市,由于国家的特殊要求,每两个城市之间存在一条唯一的简单通路。
国王希望借着这个机会视察Tar国的城市建设,因此他提出S到T的距离不能少于L条道路。
同时,国王的私人医生检查了他的身体情况后,断定国王的身体不适合做长途旅行,因此他要求S到T的距离不能多于R条道路。
另外,政府希望跟随国王的人民沿途不仅能看到城市风景,还能看到城市外的美丽乡村。因此每条道路定义了一个魅力值Ci,一条路径的魅力值定义为这条路径的中位数。更详细的说法是这样的:
将路径上所有边的魅力值排序,得到序列{Ai}。假设i=2k+c(0<=c<=1),中位数就是A(k+1)。
你的任务就是求出魅力值最大的路径,并输出这个魅力值。
Input
第一行是三个整数N,L,R,表示Tar国的城市个数、路径的最小和最大长度。
接下来N-1行,每行3个整数Ai,Bi,Ci,表示有一条连接Ai和Bi且魅力值Ci的道路。
Output
仅一行,表示最大的魅力值。如果不存在这样的路径,输出-1。
Sample Input
5 1 4
1 2 1
1 3 4
3 4 7
3 5 2
1 2 1
1 3 4
3 4 7
3 5 2
Sample Output
7
HINT
对于100%的数据:N<=100000,1<=L<=R<=N-1,1<=Ci<=1000000000
【解题思路】
首先用二分答案把题目转化成在树中能否找到一条中位数>=mid的合法路径的判定性问题。
把<mid的边权标记-1,其他的标记1,则对于一条标记和>=0的合法路径,有中位数>=mid
用树分治解决,记录每个点的深度(到重心的距离)和到重心路径的标记和
O(Nlog³N)
因为与某个节点可以构成合法路径的节点是连续的,所以可以用线段树快速寻找最优路径
线段树清空记得用标记
O(Nlog²N)
对于已处理子树,每个深度我们只需要记录一个最优节点的信息best[]
还是利用“连续”性质,bfs遍历新子树并用单调队列O(1)寻找最优匹配点
【优化】
对于每次二分的每个重心计算,我们都要清空一次best[]
所以为了节省时间,在二分求答案之前,先进行一次树分治,一是把子树按最大深度从小到大排序,使best[]的有效范围变成逐渐扩大的,二是将重心顺序存下免去后续冗余计算
对于子树大小<=l的重心没必要处理
【呆马】
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<iostream>
using namespace std;
const int N=1e5+1,inf=1e9;
int n,m,cnt,x,y,z,i,l,r,L,R,mid,V,ans,dhe,dta,qhe,qta,ROOT,mxd,a[N],fi[N],f[N],siz[N],root[N],son[N],v[N],bes[N];
int to[N<<1],ne[N<<1],val[N<<1],fa[N],dep[N],d[N],qd[N],qv[N];
bool vis[N];
bool cmp(int x,int y){return dep[x]<dep[y];}
void add(int x,int y,int z)
{
to[++cnt]=y; ne[cnt]=fi[x]; val[cnt]=z; fi[x]=cnt;
to[++cnt]=x; ne[cnt]=fi[y]; val[cnt]=z; fi[y]=cnt;
}
void getroot(int x,int fa,int num)
{
int y,i;
f[x]=0,siz[x]=1;
for (y=to[i=fi[x]];i;y=to[i=ne[i]])
if (!vis[y] && y!=fa)
{
getroot(y,x,num);
f[x]=max(f[x],siz[y]);
siz[x]+=siz[y];
}
f[x]=max(f[x],num-siz[x]);
if (f[x]<f[ROOT]) ROOT=x;
}
int dfs(int x,int fa,int dep)
{
int Max=0;
if (dep==r) return r;
for (int i=fi[x];i;i=ne[i])
if (!vis[to[i]] && to[i]!=fa)
{
Max=max(Max,dfs(to[i],x,dep+1));
if (Max==r) return r;
}
return dep;
}
void part(int x)
{
int num=0;
vis[x]=1;
for (int i=fi[x];i;i=ne[i])
if (!vis[to[i]]) dep[son[++num]=to[i]]=dfs(to[i],x,1),v[son[num]]=val[i];
sort(son+1,son+num+1,cmp);
for (int i=fi[x],t=0;i;i=ne[i])
if (!vis[to[i]]) to[i]=son[++t],val[i]=v[son[t]];
for (int i=fi[x];i;i=ne[i])
if (!vis[to[i]] && siz[to[i]]>=l)
{
ROOT=0;
getroot(to[i],x,siz[to[i]]);
part(root[++m]=ROOT);
}
}
bool bfs()
{
int x,y;
for (;dhe<=dta;dhe++)
{
x=d[dhe];
y=dep[dhe];
if (dhe!=1 && y!=dep[dhe-1])
{
if (y+qd[qhe]>r) qhe++;
int t=l-y;
if (t>0) t=bes[t];
if (t>-inf)
{
for (;qhe<=qta && t>=qv[qta];qta--);
qd[++qta]=l-y;
qv[qta]=t;
}
}
if ((qhe<=qta && v[x]+qv[qhe]>=0) || (y>=l && v[x]>=0)) return 1;
if (y<r)
{
for (int i=fi[x];i;i=ne[i])
if (!vis[to[i]] && to[i]!=fa[x])
{
d[++dta]=to[i];
dep[dta]=y+1;
if (val[i]<V) v[d[dta]]=v[x]-1;
else v[d[dta]]=v[x]+1;
fa[d[dta]]=x;
}
}
}
return 0;
}
bool find(int x)
{
for (int i=1;i<=mxd;i++) bes[i]=-inf;
vis[x]=1;
for (int i=fi[x];i;i=ne[i])
if (!vis[to[i]])
{
qhe=1,qta=0;
for (int j=mxd;j>=l-1;j--)
if (bes[j]>-inf)
{
for (;qhe<=qta && qv[qta]<=bes[j];qta--);
qd[++qta]=j;
qv[qta]=bes[j];
}
dep[dhe=dta=1]=1;
d[1]=to[i];
if (val[i]<V) v[d[1]]=-1;
else v[d[1]]=1;
fa[d[1]]=x;
if (bfs()) return 1;
mxd=max(mxd,dep[dta]);
for (int j=1;j<=dta;j++) bes[dep[j]]=max(bes[dep[j]],v[d[j]]);
}
return 0;
}
int main()
{
scanf("%d%d%d\n",&n,&l,&r);
for (i=1;i<n;i++)
{
scanf("%d%d%d\n",&x,&y,&z);
a[i]=z;
add(x,y,z);
}
sort(a+1,a+n);
R=unique(a+1,a+n)-(a+1);
f[0]=inf;
getroot(1,0,n);
part(root[m=1]=ROOT);
ans=-1;
for (i=1;i<=n;i++) bes[i]=-inf;
for (L=1;L<=R;)
{
for (i=1;i<=n;i++) vis[i]=0;
mid=(L+R)>>1;
V=a[mid];
bool bo=0;
for (i=1;i<=m;i++)
if (find(root[i]))
{
bo=1;
break;
}
if (bo) L=mid+1,ans=V;
else R=mid-1;
}
printf("%d",ans);
}