题目描述
给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)
输入
第一行给出N,S,E。N代表树的点数,S,E如题目描述。
下面N-1行给出这棵树的相邻两个节点的边及其权值W。
输出
输出共一行一个整数,表示答案。若无解输出-1。
样例输入
5 10 40
2 4 80
2 3 57
1 2 16
2 5 49
样例输出
16
样例解释
1到2的路径即为答案。
数据范围
对于20%的数据满足n<=300
对于50%的数据满足n<=3000
对于60%的数据满足n<=10^5
对于以上数据,满足|E-S|<=50
对于100%的数据满足n<=10^5,|E-S|<=10^6
对于所有数据满足1<=Wi<=1000,|E|,|S|<=10^9
解法
树上路径问题使用树分治解决。
对于当前树,维护一个Dis数组表示,当前树中的每个点到当前树根结点的距离。
依次遍历当前根结点的子树,得出来一个dis表示这个子树的结点到当前根结点的距离。考虑合并Dis和dis,对于每个dis[i],在Dis中二分出一个Dis[j]使得Dis[j]+dis[i]>=S,然后更新答案。
最后把dis并入Dis维护有序性即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define sqr(x) ((x)*(x))
#define ln(x,y) int(log(x)/log(y))
using namespace std;
const char* fin="aP2.in";
const char* fout="aP2.out";
const int inf=0x7fffffff;
const int maxn=100007,maxm=2*maxn,maxde=31;
int n,m1,m2,i,j,k,l,tot,siz,root,ans=inf;
int fi[maxn],la[maxm],va[maxm],ne[maxm];
int dis[maxn],Dis[maxn];
bool bz[maxn];
void add_line(int a,int b,int c){
tot++;
ne[tot]=fi[a];
la[tot]=b;
va[tot]=c;
fi[a]=tot;
}
void getsize(int v,int from){
int i,j,k;
if (!from) siz=0;
siz++;
for (k=fi[v];k;k=ne[k]) if (!bz[la[k]] && la[k]!=from) getsize(la[k],v);
}
int getroot(int v,int from){
int i=1,j=1,k,tmp;
for (k=fi[v];k;k=ne[k]){
if (!bz[la[k]] && la[k]!=from) {
tmp=getroot(la[k],v);
if (tmp>siz/2) i=0;
j+=tmp;
}
}
if (i && j>siz/2) root=v;
return j;
}
void getdis(int v,int from,int st){
int i,j,k;
if (st>m2) return;
dis[++dis[0]]=st;
for (k=fi[v];k;k=ne[k]){
if (la[k]!=from && !bz[la[k]]){
getdis(la[k],v,st+va[k]);
}
}
}
void merge(){
int i,j,k,l,r,mid;
for (i=1;i<=dis[0];i++){
l=1;
r=Dis[0];
while (l<r){
mid=(l+r)/2;
if (dis[i]+Dis[mid]>=m1) r=mid;
else l=mid+1;
}
if (dis[i]+Dis[l]>=m1) ans=min(ans,dis[i]+Dis[l]);
}
for (i=1;i<=dis[0];i++) Dis[++Dis[0]]=dis[i];
sort(Dis+1,Dis+Dis[0]+1);
}
void dfs(int v,int de){
int i,j,k;
Dis[0]=1;
Dis[1]=0;
bz[v]=true;
for (k=fi[v];k;k=ne[k]){
if (!bz[la[k]]){
dis[0]=0;
getdis(la[k],v,va[k]);
merge();
}
}
for (k=fi[v];k;k=ne[k])
if (!bz[la[k]]){
getsize(la[k],0);
getroot(la[k],0);
dfs(la[k],de+1);
}
}
int main(){
scanf("%d%d%d",&n,&m1,&m2);
for (i=1;i<n;i++){
scanf("%d%d%d",&j,&k,&l);
add_line(j,k,l);
add_line(k,j,l);
}
getsize(1,0);
getroot(1,0);
dfs(1,0);
if (ans>m2) printf("-1");
else printf("%d",ans);
return 0;
}
启发
树上路径问题使用树上分治。
树上分治处理方法并不单一,须灵活运用。