• bzoj4738: 汽水


    Description

     牛牛来到了一个盛产汽水的国度旅行。这个国度的地图上有n个城市,这些城市之间用n?1条道路连接,任意两个城

    市之间,都存在一条路径连接。这些城市生产的汽水有许多不同的风味,在经过道路 i 时,牛牛会喝掉wi的汽水
    。牛牛非常喜欢喝汽水,但过量地饮用汽水是有害健康的,因此,他希望在他旅行的这段时间内,平均每天喝到的
    汽水的量尽可能地接近给定的一个正整数k。同时,牛牛希望他的旅行计划尽可能地有趣,牛牛会先选择一个城市
    作为起点,然后每天通过一条道路,前往一个没有去过的城市,最终选择在某一个城市结束旅行。牛牛还要忙着去
    喝可乐,他希望你帮他设计出一个旅行计划,满足每天 |平均每天喝到的汽水-k| 的值尽量小(其中天数至少为1
    ),请你告诉他这个最小值。

    Input

    第一行两个正整数n,k。
    接下来n-1行,每行三个正整数ui,vi,wi,表示城市ui和城市vi之间有一条长度为wi的道路连接。
    n≤5×10^4,0≤wi≤10^13,0≤k≤10^13。
    同一行相邻的两个整数均用一个空格隔开。

    Output

    一行一个整数,表示 |平均每天喝到的汽水-k|的最小值的整数部分
    即你只要将这个最小值向下取整然后输出即可。

    类似一般的分数规划,二分答案,转化为是否存在一条路径,两个权值均为正数

    点分治并用平衡树维护一下已处理路径的答案

    时间复杂度$O(nlog^2(n)log(max(a_i)))$

    #include<cstdio>
    #include<algorithm>
    #include<cstdlib>
    typedef long long i64;
    template<class T>
    void _(T&x){
        x=0;
        int c=getchar(),f=1;
        while(c<48)c=='-'&&(f=-1),c=getchar();
        while(c>47)x=x*10+c-48,c=getchar();
        x*=f;
    }
    const int N=50007;
    int n;i64 k;
    int es[N*2],enx[N*2],e0[N],ep=2;
    i64 ev[N*2];
    i64 _0=0,_1=0,maxv=1ll<<61,now;
    int _2=0;
    i64 abs(i64 x){return x>0?x:-x;}
    void mins(i64&a,i64 b){if(a>b)a=b;}
    void f0(int w,int pa){
        if(_2)mins(maxv,abs(_1-k*_2)/_2);
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(u==pa)continue;
            _1+=ev[i],++_2;
            f0(u,w);
            _1-=ev[i],--_2;
        }
    }
    bool ed[N],flag;
    int sz[N],SZ,CG;
    void f2(int w,int pa){
        sz[w]=1;
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(u==pa||ed[u])continue;
            f2(u,w);
            sz[w]+=sz[u];
        }
    }
    void f3(int w,int pa){
        bool is=sz[w]*2>=SZ;
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(u==pa||ed[u])continue;
            f3(u,w);
            if(sz[u]*2>SZ)is=0;
        }
        if(is)CG=w;
    }
    struct pos{
        i64 x,y;
    }vs[N];
    struct node{
        pos w;
        node*lc,*rc;
        int rnd;
        void init(pos _w){
            w=_w;
            rnd=rand()|1;
            lc=rc=0;
        }
    }mem[N],*ms[N],*rt;
    int mp=0;
    node*merge(node*a,node*b){
        if(!a)return b;
        if(!b)return a;
        if(a->rnd>b->rnd){
            a->rc=merge(a->rc,b);
            return a;
        }{
            b->lc=merge(a,b->lc);
            return b;
        }
    }
    void splitx(node*w,node*&l,node*&r,i64 x){
        if(!w){
            l=r=0;
            return;
        }
        if(x<w->w.x){
            splitx(w->lc,l,w->lc,x);
            r=w;
        }else{
            l=w;
            splitx(w->rc,w->rc,r,x);
        }
    }
    void splity(node*w,node*&l,node*&r,i64 x){
        if(!w){
            l=r=0;
            return;
        }
        if(x<w->w.y){
            splity(w->lc,l,w->lc,x);
            r=w;
        }else{
            l=w;
            splity(w->rc,w->rc,r,x);
        }
    }
    void rec(node*&w){
        if(!w)return;
        rec(w->lc);
        rec(w->rc);
        ms[mp++]=w;
        w=0;
    }
    bool chk(node*w,i64 x,i64 y){
        while(w){
            if(w->w.x<=x)w=w->rc;
            else{
                if(w->w.y<y)return 1;
                w=w->lc;
            }
        }
        return 0;
    }
    int vp=0;
    void f4(int w,int pa,i64 v1,i64 v2,int dep){
        if(v1>0&&v2<0)flag=1;
        else{
            if(chk(rt,-v1,-v2))flag=1;
            vs[vp++]=(pos){v1,v2};
        }
        if(!flag)
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(u==pa||ed[u])continue;
            f4(u,w,v1+ev[i]-k+now,v2+ev[i]-k-now,dep+1);
        }
    }
    int cs[N],cp,ee[N];
    bool cmp(int a,int b){
        return sz[a]<sz[b];
    }
    bool chk(i64 M){
        flag=0;
        now=M+1;
        rec(rt);
        for(int i=1;i<=cp;++i){
            int w=cs[i];
            vp=0;
            f4(w,0,ev[ee[w]]-k+now,ev[ee[w]]-k-now,1);
            if(flag){
                maxv=M;
                return 1;
            }
            if(i==cp)break;
            while(vp){
                pos w=vs[--vp];
                if(chk(rt,w.x,w.y+1))continue;
                node*p1,*p2,*p3;
                splitx(rt,p1,p2,w.x);
                splity(p1,p1,p3,w.y);
                rec(p3);
                node*a=ms[--mp];
                a->init(w);
                rt=merge(p1,merge(a,p2));
            }
        }
        return 0;
    }
    void f1(int w){
        f2(w,0);SZ=sz[w];
        f3(w,0);w=CG;
        f2(w,0);
        ed[w]=1;
        cp=0;
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(!ed[u])cs[++cp]=u,ee[u]=i;
        }
        if(maxv>0&&chk(maxv-1)){
            i64 L=0,R=maxv-1,M;
            while(L<=R){
                M=L+R>>1;
                if(chk(M))R=M-1;
                else L=M+1;
            }
        }
        for(int i=e0[w];i;i=enx[i]){
            int u=es[i];
            if(!ed[u])f1(u);
        }
    }
    int main(){
        _(n);_(k);
        for(int i=0;i<n;++i)ms[mp++]=mem+i;
        for(int i=1;i<n;++i){
            int a,b;i64 c;
            _(a);_(b);_(c);
            es[ep]=b;enx[ep]=e0[a];ev[ep]=c;e0[a]=ep++;
            es[ep]=a;enx[ep]=e0[b];ev[ep]=c;e0[b]=ep++;
        }
        for(int i=1;i<=n;++i)if(rand()%10000==1||i==1)f0(i,0);
        f1(1);
        printf("%lld",maxv);
        return 0;
    }
  • 相关阅读:
    【项目一 · 直播】 ☞ 3. 推流直播
    【项目一 · 直播】 ☞ 2. 拉流直播
    【Objective-C 篇】 ☞ 10. 代理设计模式
    高精度模板
    csp每日习题
    pat乙级每日习题
    dp-LIS LCS 模型
    线性dp
    归并排序应用-求逆序对数量
    dp-位移模型(数字三角形演变)
  • 原文地址:https://www.cnblogs.com/ccz181078/p/6768797.html
Copyright © 2020-2023  润新知