题目描述
给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)
n<=10^5,|E-S|<=10^6,1<=Wi<=1000,|E|,|S|<=10^9
怎么看都是点剖。
而对k有两个限制,不能直接求最小值,但是可以求在这个区间范围内的k有多少个。
二分+点剖
二分E的值,求在这个区间范围内的k的数量,若>0则合法。
只有第一次点剖才排序,然后保存排序后的数值,这样复杂度为O(
nlog2n
),否则复杂度是O(
nlog3n
)
代码
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=100000+100;
int i,j,n,d[maxn],f[maxn],s[maxn],root,mid,ss,l,r,t,ro[maxn];
ll ans;
int q[maxn*50],st[maxn*50],en[maxn*50],tt;
int k[maxn],g[maxn*2],next[maxn*2],c[maxn*2],num,cnt;
int k1[maxn],g1[maxn*2],next1[maxn*2],b;
bool bz[maxn];
void add(int x,int y,int z){next[++num]=k[x];k[x]=num;g[num]=y;c[num]=z;}
void add1(int x,int y){next1[++cnt]=k1[x];k1[x]=cnt;g1[cnt]=y;}
void dfs(int x,int y){
s[x]=1,f[x]=0;
int i=k[x];
while (i){
if ((!bz[g[i]])&&(g[i]!=y)) dfs(g[i],x),s[x]+=s[g[i]],f[x]=max(f[x],s[g[i]]);
i=next[i];
}
f[x]=max(f[x],f[0]-s[x]);if (f[x]<f[root]) root=x;
}
void df(int x,int y){
if (d[x]>mid) return;
q[++num]=d[x];
int i=k[x];
while (i){
if ((!bz[g[i]])&&(g[i]!=y)) d[g[i]]=d[x]+c[i],df(g[i],x);
i=next[i];
}
}
ll chu(int x){
tt++;
if (st[tt]==0) {
st[tt]=num+1,df(x,0),en[tt]=num;
sort(q+st[tt],q+1+en[tt]);
}
int i,j1=en[tt],j=en[tt];ll w=0;
fo(i,st[tt],en[tt]-1){
while (q[i]+q[j]>mid&&j>=i) j--;if (j<=i) break;
if (j1==i) j1=i+1;else
while ((q[i]+q[j1-1]>=ss)&&(j1-1>i)) j1--;
if ((q[i]+q[j1]<ss)||(j<j1)) continue;
w+=j-j1+1;
}
return w;
}
void fen(int x){
bz[x]=1,d[x]=0,b=0,ans+=chu(x);b=1;
int i=k[x];
while (i){
if (!bz[g[i]]) add1(x,g[i]),ans-=chu(g[i]);
i=next[i];
}i=k1[x];
while (i){
int go=g1[i];
f[root=0]=s[go],dfs(go,0),ro[++t]=root;
fen(ro[t]);
i=next1[i];
}
}
void fen1(int x){
if (ans) return;
b=0,ans+=chu(x);
int i=k1[x];b=1;
while (i) {
ans-=chu(g1[i]);
i=next1[i];
}
i=k1[x];while (i)fen1(ro[++t]),i=next1[i];
}
int main(){
scanf("%d%d%d",&n,&ss,&r);l=ss,mid=r;
fo(i,1,n-1) {int x,y,z;scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,z);}
t=0;f[root=0]=n;
dfs(1,0);ro[t]=root;num=tt=0;
fen(root);
if (ans==0) {printf("-1\n");return 0;}
while (l<r){
mid=(l+r)/2,t=tt=ans=0,fen1(ro[t]);
if (ans) r=mid;else l=mid+1;
}
printf("%d\n",l);
}