1. 程式人生 > >洛谷 P4292 [WC2010]重建計劃 解題報告

洛谷 P4292 [WC2010]重建計劃 解題報告

題目 ger 正整數 輸入 政府 += else 個數 fine

P4292 [WC2010]重建計劃

題目描述

\(X\)國遭受了地震的重創, 導致全國的交通近乎癱瘓,重建家園的計劃迫在眉睫。\(X\)國由\(N\)個城市組成, 重建小組提出,僅需建立\(N-1\)條道路即可使得任意兩個城市互相可達。於是,重建小組很快提出了一個包含\(N-1\)條道路的方案,並滿足城市之間兩兩可達,他們還計算評估了每條道路\(e\)建設之後可以帶來的價值\(v(e)\)

由於重建計劃復雜而艱難,經費也有一定限制。因此,政府要求第一期重建工程修建的道路數目為\(k\)條,但需滿足\(L ≤ k ≤ U\), 即不應少於\(L\)條,但不超過\(U\)條。同時,為了最大化利用率,要求建設的這些道路恰好組成一條簡單路徑,即所建設的\(k\)

條路徑可以構成一個排列\(e_1 = (p_1, q_1), e_2 = (p_2, q_2),\dots, e_k = (p_k, q_k)\), 對於 \(1 ≤ i < k\), 有\((q_i = p_i+1)\)

重建小組打算修改他們的原有方案以滿足要求,即在原有的N-1條道路中尋找一條路徑S作為新的方案,使得新方案中的道路平均價值

\[AvgValue = \frac{\sum _{e \in S} v(e)}{|S|}\]

最大。這裏\(v(e)\)表示道路\(e\)的價值,\(|S|\)表示新方案中道路的條數。請你幫助重建小組尋找一個最優方案。 註: 在本題中\(L\)

\(U\)的設置將保證有解。

輸入輸出格式

輸入格式:

第一行包含一個正整數\(N\),表示\(X\)國的城市個數。

第二行包含兩個正整數\(L\)\(U\),表示政府要求的第一期重建方案中修建道路數的上下限。

接下來的\(N-1\)行描述重建小組的原有方案,每行三個正整數\(a_i, b_i, v_i\),分別表示道路\((a_i, b_i)\),其價值為\(v_i\)。其中城市由\(1\dots N\)標號。

輸出格式:

僅包含一行,為一個實數\(AvgValue\),即最大平均價值。

小數點後保留三位。

說明

對於\(20\%\)的數據,\(N ≤ 5 000\);

另有\(30\%\)的數據,\(N ≤ 100 000\)

, 原有方案恰好為一條路徑(鏈);

對於\(100\%\)的數據,\(N ≤ 100 000, 1 ≤ L ≤ U ≤ N-1, v_i ≤ 10^6\)


鑒於本辣雞代碼寫的天昏地暗,把自己惡心的不行,所以思路也懶得好好說了,簡單說一下吧。

分數規劃+線段樹維護長鏈剖分

二分之後需要找一條長度為\([l,r]\)之間的鏈的邊權和大於\(0\)

顯然可以\(O(n^2)\)進行\(dp\),然後使用長鏈剖分繼承重兒子。

發現繼承重兒子的過程需要開一個線段樹維護。

只開一個線段樹維護\(dfs\)序,然後就可以很方便的進行偏移,加連接重兒子的邊時可以直接區間打\(tag\)


Code:

// luogu-judger-enable-o2
#include <cstdio>
#include <algorithm>
const int N=1e5+10;
int head[N],to[N<<1],Next[N<<1],cnt;
double edge[N<<1];
void add(int u,int v,double w)
{
    to[++cnt]=v,Next[cnt]=head[u],edge[cnt]=w,head[u]=cnt;
}
double tag[N<<2],mx[N<<2];
int n,lp,rp;
using std::min;
using std::max;
#define ls id<<1
#define rs id<<1|1
void pushdown(int id)
{
    if(tag[id]<-1e17) return;
    if(mx[ls]<-1e17) mx[ls]=tag[id];
    else mx[ls]+=tag[id];
    if(mx[rs]<-1e17) mx[rs]=tag[id];
    else mx[rs]+=tag[id];
    if(tag[ls]<-1e17) tag[ls]=tag[id];
    else tag[ls]+=tag[id];
    if(tag[rs]<-1e17) tag[rs]=tag[id];
    else tag[rs]+=tag[id];
    tag[id]=-1e18;
}
double query(int id,int L,int R,int l,int r)
{
    if(L==l&&R==r) return mx[id];
    pushdown(id);
    int Mid=L+R>>1;
    if(r<=Mid) return query(ls,L,Mid,l,r);
    else if(l>Mid) return query(rs,Mid+1,R,l,r);
    else return max(query(ls,L,Mid,l,Mid),query(rs,Mid+1,R,Mid+1,r));
}
void change(int id,int L,int R,int l,int r,double d)
{
    if(L==l&&R==r)
    {
        if(mx[id]<-1e17) mx[id]=d;
        else mx[id]+=d;
        if(tag[id]<-1e17) tag[id]=d;
        else tag[id]+=d;
        return;
    }
    pushdown(id);
    int Mid=L+R>>1;
    if(r<=Mid) change(ls,L,Mid,l,r,d);
    else if(l>Mid) change(rs,Mid+1,R,l,r,d);
    else change(ls,L,Mid,l,Mid,d),change(rs,Mid+1,R,Mid+1,r,d);
    mx[id]=max(mx[ls],mx[rs]);
}
void modify(int id,int l,int r,int p,double d)
{
    if(l==r) {mx[id]=max(mx[id],d);return;}
    pushdown(id);
    int mid=l+r>>1;
    if(p<=mid) modify(ls,l,mid,p,d);
    else modify(rs,mid+1,r,p,d);
    mx[id]=max(mx[ls],mx[rs]);
}
int dis[N],ws[N],dfn[N],dfsclock;
double ans;
void dfsinit(int now,int fa)
{
    for(int v,i=head[now];i;i=Next[i])
        if((v=to[i])!=fa)
        {
            dfsinit(v,now);
            if(dis[to[ws[now]]]<dis[v]) ws[now]=i;
        }
    dis[now]=dis[to[ws[now]]]+1;
}
int tn,ty;
void dfsseg(int id,int L,int R,int l,int r,int p)
{
    if(L==R)
    {
        int len=l+1-tn;
        if(ty) modify(1,1,n,len+dfn[p],mx[id]);
        else if(len+dis[p]-1>=lp&&len<rp)
            ans=max(ans,mx[id]+query(1,1,n,max(dfn[p]+1,dfn[p]+lp-len),min(dfn[p]+rp-len,dfn[p]+dis[p]-1)));
        return;
    }
    pushdown(id);
    int Mid=L+R>>1;
    if(r<=Mid) dfsseg(ls,L,Mid,l,r,p);
    else if(l>Mid) dfsseg(rs,Mid+1,R,l,r,p);
    else dfsseg(ls,L,Mid,l,Mid,p),dfsseg(rs,Mid+1,R,Mid+1,r,p);
}
void dfs(int now,int fa)
{
    dfn[now]=++dfsclock;
    if(ws[now])
    {
        dfs(to[ws[now]],now);
        change(1,1,n,dfn[now]+1,dfn[now]+1,edge[ws[now]]);
        if(dis[now]>2)
            change(1,1,n,dfn[now]+2,dfn[now]+dis[now]-1,edge[ws[now]]);
    }
    for(int v,i=head[now];i;i=Next[i])
        if((v=to[i])!=fa&&i!=ws[now])
        {
            dfs(v,now);tn=dfn[v];
            change(1,1,n,dfn[v],dfn[v]+dis[v]-1,edge[i]);
            ty=0,dfsseg(1,1,n,dfn[v],dfn[v]+dis[v]-1,now);
            ty=1,dfsseg(1,1,n,dfn[v],dfn[v]+dis[v]-1,now);
        }
    if(dis[now]-1>=lp)
        ans=max(ans,query(1,1,n,dfn[now]+lp,min(dfn[now]+rp,dfn[now]+dis[now]-1)));
}
bool check(double d)
{
    for(int i=1;i<=cnt;i++) edge[i]-=d;
    for(int i=1;i<=n<<2;i++) mx[i]=tag[i]=-1e18;
    dfsclock=0,ans=-1e18;dfs(1,0);
    for(int i=1;i<=cnt;i++) edge[i]+=d;
    return ans>=0;
}
int main()
{
    scanf("%d%d%d",&n,&lp,&rp);
    double l=1e18,r=-1e18,w;
    for(int u,v,i=1;i<n;i++)
    {
        scanf("%d%d%lf",&u,&v,&w),add(u,v,w),add(v,u,w);
        l=min(l,w),r=max(r,w);
    }
    dfsinit(1,0);
    while(l+1e-4<r)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%.3lf\n",l);
    return 0;
}

2018.12.14

洛谷 P4292 [WC2010]重建計劃 解題報告