[WC2010]重建計劃

給定一棵n個點的樹,每條邊有權值。
求一條鏈,這條鏈包含的邊數在L和U之間,且平均邊權最大。

類似於分數規劃問題,轉換爲二分答案
將每條邊減去二分值,判斷是否有權值和大於等於0的鏈
處理出根到其中一個子樹中每種深度的最大權值和
另外記錄一下根到已知子樹中每種深度的最大權值和
就變成了定區間求最值,用單調隊列即可

#include<iostream>
#include<cstdio>
using namespace std;
int head[100005],vet[200005],nxt[200005],w[200005],num;
bool vis[100005];
int sz[100005],mxs[100005],root,sz1,sz2;
int que[100005];
double mxr,ans,mid,d1[100005],d2[100005];
int L,U,n;
inline int read() {
    char ch=0;int sum=0;
    while (ch>'9'||ch<'0') ch=getchar();
    while (ch>='0'&&ch<='9') sum=sum*10+ch-'0',ch=getchar();
    return sum;
}
inline void addedge(int x,int y,int c) {
    num++;vet[num]=y;nxt[num]=head[x];w[num]=c;head[x]=num;
    num++;vet[num]=x;nxt[num]=head[y];w[num]=c;head[y]=num;
}
void getdepth(int x,int ff,int dep,double sum) {
    if (dep>sz2) {
        sz2=dep;
        d2[dep]=-1e11;
    }
    d2[dep]=max(d2[dep],sum);
    for (int e=head[x];e;e=nxt[e]) {
        if (vet[e]==ff||vis[vet[e]]) continue;
        getdepth(vet[e],x,dep+1,sum+w[e]-mid);
    }
}
inline double calc() {
    int h=1,t=0;
    int i=min(U,sz2),j=0;
    double mx=-1e11;
    for (;i;i--) {
        while (j<=sz1&&i+j<=U) {
            while (h<=t&&d1[que[t]]<=d1[j]) t--;
            que[++t]=j;
            j++;
        }
        while (h<=t&&que[h]+i<L) h++;
        if (h>t) return mx;
        mx=max(mx,d1[que[h]]+d2[i]);
        if (mx>=0) return mx;
    }
    return mx;
}
inline bool check(int x) {
    sz1=sz2=0;
    for (int e=head[x];e;e=nxt[e]) {
        int v=vet[e];
        if (vis[v]) continue;
        getdepth(v,x,1,w[e]-mid);
        if (calc()>=0) return true;
        for (int i=1;i<=sz2;i++) {
            if (i>sz1) d1[i]=-1e11;
            d1[i]=max(d1[i],d2[i]);
            d2[i]=-1e11;
        }
        sz1=max(sz1,sz2);
        sz2=0;
    }
    return false;
}
void bs(int x) {
    double l=ans,r=mxr;
    while (r-l>=0.0001) {
        mid=(l+r)/2;
        if (check(x)) l=mid;
        else r=mid;
    }
    ans=max(ans,l);
}
void getroot(int x,int ff,int tt) {
    sz[x]=1;mxs[x]=0;
    for (int e=head[x];e;e=nxt[e]) {
        int v=vet[e];
        if (vis[v]||v==ff) continue;
        getroot(v,x,tt);
        sz[x]+=sz[v];
        mxs[x]=max(mxs[x],sz[v]);
    }
    mxs[x]=max(mxs[x],tt-sz[x]);
    if (mxs[root]>mxs[x]) root=x;
}
void solve(int x,int tot) {
    bs(x);
    vis[x]=1;
    for (int e=head[x];e;e=nxt[e]) {
        if (vis[vet[e]]) continue;
        int tt=sz[vet[e]];root=0;
        if (tt>sz[x]) tt=tot-sz[x];
        getroot(vet[e],x,tt);
        solve(root,tt);
    }
}
int main() {
    n=read();L=read();U=read();
    for (int i=1;i<n;i++) {
        int x=read(),y=read(),c=read();
        addedge(x,y,c);
        mxr=max(mxr,(double)c);
    }
    for (int i=1;i<=n;i++) d1[i]=d2[i]=-1e11;
    mxs[0]=1e9;
    getroot(1,0,n);
    solve(root,n);
    printf("%.3lf",ans);
    return 0;
}

蒟蒻用dfs算距離
dalao用bfs算距離
http://hzwer.com/5362.html
蒟蒻的常數肯定比dalao大 orz

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章