[BZOJ]1758: [Wc2010]重建计划

Posted ditoly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ]1758: [Wc2010]重建计划相关的知识,希望对你有一定的参考价值。

题目大意:给定一棵n个点的带边权的树和l,u,求长度在[l,u]之间平均权值最大的链的权值。(n<=100,000)

思路:二分答案,把树上每条边减去二分出的答案,点分治check是否有长度在[l,u]之间权值和大等0的链,每次把每棵子树按深度排序,记下各个深度到根距离最大的节点,再用单调队列统计即可,总复杂度O(nlogn^2)。此题卡常差评,重心只要算一次,不用每次二分都算,稍微卡卡就过了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<0||c>9);
    for(x=c-0;(c=getchar())>=0&&c<=9;)x=(x<<3)+(x<<1)+c-0;
    return x;
}
#define MN 100000
struct edge{int nx,t,w;}e[MN*2+5];
int n,a,b,h[MN+5],en,sz,ms,u[MN+5],s[MN+5],ht[MN+5],d[MN+5];
struct que{int nx,t;}qu[MN+5];
int qh[MN+5],qun,q[MN+5],qn,dq[MN+5],ql,qr,cnt,rt[MN+5];
double w,dis[MN+5],mxd[MN+5];
inline void ins(int x,int y,int w)
{
    e[++en]=(edge){h[x],y,w};h[x]=en;
    e[++en]=(edge){h[y],x,w};h[y]=en;
}
void dfs(int x)
{
    s[x]=ht[x]=u[x]=1;
    for(int i=h[x];i;i=e[i].nx)if(!u[e[i].t])
    {
        dis[e[i].t]=dis[x]+e[i].w-w;
        d[e[i].t]=d[x]+1;
        dfs(e[i].t);
        s[x]+=s[e[i].t];
        ht[x]=max(ht[x],ht[e[i].t]+1);
    }
    u[x]=0;
}
void getrt(int x)
{
    int mx=0,i;u[x]=1;
    for(i=h[x];i;i=e[i].nx)if(!u[e[i].t])
        getrt(e[i].t),mx=max(mx,s[e[i].t]);
    mx=max(mx,sz-s[x]);
    if(mx<ms)ms=mx,rt[cnt]=x;
    u[x]=0;
}
bool solve(int x)
{
    int i,k,l,p=0,o,mx=0;
    u[x]=1;
    for(i=h[x];i;i=e[i].nx)if(!u[e[i].t])
    {
        dis[e[i].t]=e[i].w-w;
        d[e[i].t]=1;
        dfs(e[i].t);
        mx=max(mx,ht[e[i].t]);
    }
    memset(qh,qun=0,sizeof(int)*(mx+1));
    for(i=h[x];i;i=e[i].nx)if(!u[e[i].t])
        qu[++qun]=(que){qh[ht[e[i].t]],e[i].t},qh[ht[e[i].t]]=qun;
    for(i=1;i<=mx;++i)
    {
        mxd[i]=-1e18;
        for(int&j=qh[i];j;p=i,j=qu[j].nx)
        {
            for(u[q[k=qn=ql=0]=qu[j].t]=1,qr=-1;k<=qn;++k)
            {
                while(p>=0&&d[q[k]]+p>=a)
                {
                    while(ql<=qr&&mxd[p]>=mxd[dq[qr]])--qr;
                    dq[++qr]=p--;
                }
                while(ql<=qr&&d[q[k]]+dq[ql]>b)++ql;
                if(ql<=qr&&mxd[dq[ql]]+dis[q[k]]>=0)return true;
                for(o=h[q[k]];o;o=e[o].nx)if(!u[e[o].t])u[q[++qn]=e[o].t]=1;
            }
            for(k=0;k<=qn;++k)mxd[d[q[k]]]=max(mxd[d[q[k]]],dis[q[k]]),u[q[k]]=0;
        }
    }
    for(i=h[x];i;i=e[i].nx)if(!u[e[i].t])
    {
        if(!rt[++cnt])sz=ms=s[e[i].t],getrt(e[i].t);
        if(solve(rt[cnt]))return true;
    }
    return false;
}
bool check(double x)
{
    memset(u,cnt=0,sizeof(u));
    w=x;if(!rt[0])dfs(1),sz=ms=n,getrt(1);
    return solve(rt[0]);
}
int main()
{
    int i,x,y;double l,r,mid;
    n=read();a=read();b=read();
    for(i=1;i<n;++i)x=read(),y=read(),ins(x,y,read());
    for(l=1,r=1000000;r-l>5*1e-4;)(check(mid=(l+r)/2)?l:r)=mid;
    printf("%.3lf",(l+r)/2);
}

 

以上是关于[BZOJ]1758: [Wc2010]重建计划的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1758 [Wc2010]重建计划

bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check

[BZOJ]1758: [Wc2010]重建计划

bzoj1758Wc10重建计划——solution

BZOJ 1758WC 2010重建计划 分数规划+点分治+单调队列

[WC 2010]重建计划