bzoj1758 [Wc2010]重建计划 长链剖分+线段树

90 篇文章 0 订阅
2 篇文章 0 订阅

Description


X国遭受了地震的重创, 导致全国的交通近乎瘫痪,重建家园的计划迫在眉睫。X国由N个城市组成, 重建小组提出,仅需建立N-1条道路即可使得任意两个城市互相可达。于是,重建小组很快提出了一个包含N-1条道路的方案,并满足城市之间两两可达,他们还计算评估了每条道路e建设之后可以带来的价值v(e)。

由于重建计划复杂而艰难,经费也有一定限制。因此,政府要求第一期重建工程修建的道路数目为k条,但需满足L ≤ k ≤ U, 即不应少于L条,但不超过U条。同时,为了最大化利用率,要求建设的这些道路恰好组成一条简单路径,即所建设的k条路径可以构成一个排列e1 = (p1, q1), e2 = (p2, q2), „, ek = (pk, qk), 对于 1 ≤ i < k, 有(qi = pi+1)。

重建小组打算修改他们的原有方案以满足要求,即在原有的N-1条道路中寻找一条路径S作为新的方案,使得新方案中的道路平均价值

AvgValue=eSv(e)SAvgValue=eSv(e)|S|AvgValue=SeSv(e) A v g V a l u e = ∑ e ∈ S v ( e ) ∣ S ∣ A v g V a l u e = ∑ e ∈ S v ( e ) | S | A v g V a l u e =∣ S ∣ ∑ e ∈ S ​ v ( e ) ​

最大。这里v(e)表示道路e的价值,|S|表示新方案中道路的条数。请你帮助重建小组寻找一个最优方案。 注: 在本题中L和U的设置将保证有解。

对于20%的数据,N ≤ 5 000;
另有30%的数据,N ≤ 100 000, 原有方案恰好为一条路径(链);
对于100%的数据,N ≤ 100 000, 1 ≤ L ≤ U ≤ N-1, vi ≤ 10^6。

Solution


敲个二分就变成了求一条长度在[L,R]内路径的权值和最大,然后我就不会做了
首先这是一棵树,显然点分治是可写的
然鹅我并不是很想写点分治因此这里有另一种方法

先对树长链剖分,我们像做dsu on tree一样先做长链,用线段树继承长链的全部信息,然后做其他儿子
查询的时候枚举一下路径的长度len,左半边直接找长度为len的最大权值,右半边线段树查询长度为[L-len,R-len]的区间即可
一个非常巧妙的地方在于同一个长度我们只需要记录最大权值,这也是长链剖分在这类问题中优于重链剖分的原因

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

using std:: max;
using std:: min;

typedef double db;
const db INF=1e15;
const int N=400005;
const int E=600005;

struct edge {int y,w,next; db v;} e[E];

int dep[N],mx[N],son[N],pos[N],num[N];
int ls[N],edCnt,tot,L,R,n;
db rec[N<<2],dis[N],tmp[N],ans;

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void add_edge(int x,int y,int w) {
    e[++edCnt]=(edge) {y,w,ls[x],0}; ls[x]=edCnt;
    e[++edCnt]=(edge) {x,w,ls[y],0}; ls[y]=edCnt;
}

void dfs1(int now,int fa) {
    mx[now]=dep[now]=dep[fa]+1;
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==fa) continue;
        dfs1(e[i].y,now);
        mx[now]=max(mx[now],mx[e[i].y]);
        if (mx[son[now]]<mx[e[i].y]) son[now]=e[i].y;
    }
}

void dfs2(int now,int fa) {
    pos[now]=++pos[0];
    if (son[now]) dfs2(son[now],now);
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==fa||e[i].y==son[now]) continue;
        dfs2(e[i].y,now);
    }
}

void modify(int now,int tl,int tr,int x,db v) {
    rec[now]=std:: max(rec[now],v);
    if (tl==tr) return ;
    int mid=(tl+tr)>>1;
    if (x<=mid) modify(now<<1,tl,mid,x,v);
    else modify(now<<1|1,mid+1,tr,x,v);
}

db query(int now,int tl,int tr,int l,int r) {
    if (r<l) return -INF;
    if (tl==l&&tr==r) return rec[now];
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(now<<1,tl,mid,l,r);
    if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
    db qx=query(now<<1,tl,mid,l,mid);
    db qy=query(now<<1|1,mid+1,tr,mid+1,r);
    return max(qx,qy);
}

void build_tree(int now,int tl,int tr) {
    rec[now]=-INF;
    if (tl==tr) {
        num[tl]=now;
        return ;
    }
    int mid=(tl+tr)>>1;
    build_tree(now<<1,tl,mid);
    build_tree(now<<1|1,mid+1,tr);
}

void solve(int now,int fa) {
    modify(1,1,n,pos[now],dis[now]);
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==son[now]) {
            dis[e[i].y]=dis[now]+e[i].v;
            solve(e[i].y,now);
        }
    }
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].y==fa||e[i].y==son[now]) continue;
        dis[e[i].y]=dis[now]+e[i].v;
        solve(e[i].y,now);
        rep(j,1,mx[e[i].y]-dep[now]) {
            tmp[j]=rec[num[pos[e[i].y]+j-1]];
            if (j<=R) {
                db tttt=query(1,1,n,max(pos[now]+L-j,1),min(pos[now]+R-j,pos[now]+mx[now]-dep[now]));
                ans=max(ans,tttt+tmp[j]-dis[now]*2);
            }
        }
        rep(j,1,mx[e[i].y]-dep[now]) modify(1,1,n,pos[now]+j,tmp[j]);
    }
    ans=max(ans,query(1,1,n,pos[now]+L,min(pos[now]+R,pos[now]+mx[now]-dep[now]))-dis[now]);
}

int main(void) {
    n=read(),L=read(),R=read();
    rep(i,2,n) {
        int x=read(),y=read(),w=read();
        add_edge(x,y,w);
    }
    dfs1(1,0); dfs2(1,0);
    db prt,l,r; rec[0]=-INF;
    for (l=0,r=1000000;r-l>1e-5;) {
        db mid=(l+r)*0.5;
        rep(i,1,edCnt) e[i].v=-mid+e[i].w;
        ans=-INF; build_tree(1,1,n);
        solve(1,0);
        if (ans<=0) r=mid;
        else l=mid;
    }
    printf("%.3lf\n", (l+r)*0.5);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值