樹上路徑

題目描述

給出一棵樹,求出最小的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);
}
發佈了80 篇原創文章 · 獲贊 21 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章