1. 程式人生 > 實用技巧 >題解 洛谷 P4292 【[WC2010]重建計劃】

題解 洛谷 P4292 【[WC2010]重建計劃】

先二分答案 \(mid\),使得 $\frac{val}{tot} \geqslant mid $,移項得:

\[\large val - tot \times mid \geqslant 0 \]

判定對每條邊的邊權都減去 \(mid\) 後,是否存在一條邊權和大於零的路徑即可。

考慮樹形 \(DP\),設 \(f_{x,i}\) 為以 \(x\) 為根的子樹內,從 \(x\) 向下延伸 \(i\) 條邊所形成的路徑邊權和的最大值,直接 \(DP\) 複雜度是 \(O(n^2)\) 的,無法接受。

發現狀態是和深度有關,所以考慮用長鏈剖分來優化,每次轉移時,先從重兒子繼承過來,然後再將輕兒子合併,合併輕兒子時掃一遍輕兒子所在的鏈即可。因為每個輕兒子都是其所在鏈的頂端,所以每個點都只會被掃一次,複雜度就有保證了。

因為有邊數的限制,所以合併時用線段樹來維護。長鏈剖分後進行 \(dfs\),優先遍歷重兒子,求出 \(dfs\) 序。對於 \(DP\) 狀態 \(f_{x,i}\),將其用 \(dfn_x + i\) 線上段樹上表示,這樣每個狀態都有一個對應的表示,轉移時也便於合併。

#include<bits/stdc++.h>
#define maxn 200010
#define maxm 800010
#define inf 2000000000000000
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,L,R,cnt,root=1;
double now,ans,l,r;
int d[maxn],dep[maxn],len[maxn],son[maxn],dfn[maxn];
double val[maxn],dis[maxn],t[maxn],mx[maxm];
struct edge
{
    int to,nxt;
    double v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,double val)
{
    e[++edge_cnt]=(edge){to,head[from],val};
    head[from]=edge_cnt,r=max(r,val);
}
void modify(int l,int r,int pos,double v,int cur)
{
    if(l==r)
    {
        mx[cur]=max(mx[cur],v);
        return;
    }
    if(pos<=mid) modify(l,mid,pos,v,ls);
    else modify(mid+1,r,pos,v,rs);
    mx[cur]=max(mx[ls],mx[rs]);
}
double query(int L,int R,int l,int r,int cur)
{
    if(L>R) return -inf;
    if(L<=l&&R>=r) return mx[cur];
    double v=-inf;
    if(L<=mid) v=max(v,query(L,R,l,mid,ls));
    if(R>mid) v=max(v,query(L,R,mid+1,r,rs));
    return v;
}
void clear(int l,int r,int cur)
{
    mx[cur]=-inf;
    if(l==r) return;
    clear(l,mid,ls),clear(mid+1,r,rs);
}
void dfs_son(int x,int fa)
{
    d[x]=dep[x]=d[fa]+1;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa) continue;
        dfs_son(y,x),dep[x]=max(dep[x],dep[y]);
        if(dep[y]>dep[son[x]]) son[x]=y,val[son[x]]=e[i].v;
    }
    len[x]=dep[x]-d[x];
}
void dfs_dfn(int x)
{
    dfn[x]=++cnt;
    if(son[x]) dfs_dfn(son[x]);
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(dfn[y]) continue;
        dfs_dfn(y);
    }
}
void dp(int x,int fa)
{
    modify(1,n,dfn[x],dis[x],root);
    if(son[x]) dis[son[x]]=dis[x]+val[son[x]]-now,dp(son[x],x);
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa||y==son[x]) continue;
        dis[y]=dis[x]+e[i].v-now,dp(y,x);
        for(int j=1;j<=len[y]+1;++j) t[j]=query(dfn[y]+j-1,dfn[y]+j-1,1,n,root);
        for(int j=1;j<=min(len[y]+1,R);++j)
            ans=max(ans,t[j]+query(dfn[x]+L-j,min(dfn[x]+R-j,dfn[x]+len[x]),1,n,root)-2*dis[x]);
        for(int j=1;j<=len[y]+1;++j) modify(1,n,dfn[x]+j,t[j],root);
    }
    ans=max(ans,query(dfn[x]+L,min(dfn[x]+R,dfn[x]+len[x]),1,n,root)-dis[x]);
}
bool check(double m)
{
    now=m,ans=-inf,clear(1,n,root),dp(1,0);
    return ans>=0;
}
int main()
{
    read(n),read(L),read(R);
    for(int i=1;i<n;++i)
    {
        int x,y,v;
        read(x),read(y),read(v);
        add(x,y,v),add(y,x,v);
    }
    dfs_son(1,0),dfs_dfn(1);
    for(int i=1;i<=35;++i)
    {
        double m=(l+r)/2;
        if(check(m)) l=m;
        else r=m;
    }
    printf("%.3lf",l);
    return 0;
}