[BZOJ1758/WC2010]重建计划

Description
1758.jpg

Input
第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数\(A_i,B_i,V_i\)分别表示道路\((A_i,B_i)\),其价值为\(V_i\) 其中城市由1..N进行标号

Output
输出最大平均估值,保留三位小数

Sample Input
4
2 3
1 2 1
1 3 2
1 4 3

Sample Output
2.500

HINT
\(N\leqslant 100000,1\leqslant L\leqslant U\leqslant N-1,V_i\leqslant 1000000\)


考虑二分答案,然后把边权减去二分出的答案,判断是否存在>0的路径即可

点分治合并路径的时候记得按秩合并,即将子树按最深深度排序后枚举,开一个单调队列,统计完一棵子树后累计答案

二分有好几个位置,最外面、点分时、枚举子树时,放最外面常数最大(然而我就是这么写的),放在中间的位置可以随时改变二分上下界

为了卡常,可以先把点分治的重心枚举顺序先弄出来,然后类似for循环去点分治(因为luogu单点时限,Bzoj不需要)

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
    int x=0,f=1; char ch=gc();
    for (;ch<'0'||ch>'9';ch=gc())   if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline int read(){
    int x=0,f=1; char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x<0)    putchar('-'),x=-x;
    if (x>9)    print(x/10);
    putchar(x%10+'0');
}
const int N=1e5;
const double eps=1e-10,unit=1e-4;
int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],val[(N<<1)+10];
int size[N+10],Df[N+10],Root[N+10];
bool vis[N+10],flag;
double DV[N+10],Del;
int root,Max,r_sz;
int n,L,R,tot;
void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
void insert(int x,int y,int z){join(x,y,z),join(y,x,z);}
void Get_root(int x,int fa,int sz){
    int res=0; size[x]=1;
    for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
        if (son==fa||vis[son])  continue;
        Get_root(son,x,sz);
        size[x]+=size[son];
        res=max(res,size[son]);
    }
    res=max(res,sz-size[x]);
    if (res<Max)    Max=res,root=x;
}
void get_Df(int x,int fa,int deep){
    Df[x]=deep;
    for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
        if (son==fa||vis[son])  continue;
        get_Df(son,x,deep+1);
        Df[x]=max(Df[x],Df[son]);
    }
}
void get_dis(int x,int fa,double v,int Dp){
    if (Dp>R)   return;
    DV[Dp]=max(DV[Dp],v);
    for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
        if (son==fa||vis[son])  continue;
        get_dis(son,x,v+val[p]-Del,Dp+1);
    }
}
struct S1{
    int x,Deep; double v;
    oid insert(int _x,int _D,double _v){x=_x,Deep=_D,v=_v;}
    bool operator <(const S1 &tis)const{return Deep<tis.Deep;}
}A[N+10];
double f[N+10];
void solve(double *a,int &la,double *b,int &lb){
    la=min(la,R),lb=min(lb,R);
    static int h[N+10]; int head=1,tail=0;
    for (int i=lb,j=0;i;i--){
        while (j<=la&&i+j<=R){
            while (head<=tail&&a[h[tail]]<=a[j])    tail--;
            h[++tail]=j++;
        }
        while (head<=tail&&h[head]+i<L) head++;
        if (head>tail)  continue;
        if (a[h[head]]+b[i]>-eps){
            flag=1;
            return;
        }
    }
}
void merge(double *a,int &la,double *b,int &lb){for (int i=1;i<=max(la,lb);i++) a[i]=max(a[i],b[i]),b[i]=-inf; la=lb;}
void divide(int len){
    if (len>n)  return;
    int x=Root[len];
    if (flag)   return;
    is[x]=1; int son_sz=0;//num of son
    for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
        if (vis[son])   continue;
        get_Df(son,0,1),Df[son]=min(Df[son],R);
        A[++son_sz].insert(son,Df[son],val[p]-Del);
    }
    int f_sz=0;
    sort(A+1,A+1+son_sz);
    if (A[son_sz].Deep<<1<L){
        divide(len+1);
        return;
    }
    for (int i=1;i<=son_sz;i++){
        get_dis(A[i].x,0,A[i].v,1);
        if (f_sz+A[i].Deep>=L){
            solve(f,f_sz,DV,A[i].Deep);
            if (flag)   return;
        }
        merge(f,f_sz,DV,A[i].Deep);
    }
    if (flag)   return;
    for (int i=1;i<=f_sz;i++)   f[i]=-inf;
    divide(len+1);
}
bool check(double v){
    Del=v,flag=0;
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=R;i++)  DV[i]=f[i]=-inf;
    divide(1);
    return flag;
}
void Frt(int x){//Find root
    is[Root[++r_sz]=x]=1;
    for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
        if (vis[son])   continue;
        root=0,Max=inf;
        Get_root(son,0,size[son]);
        Frt(root);
    }
    is[x]=0;
}
int main(){
    n=read(),L=read(),R=read();
    double l=1e9,r=0;
    for (int i=1;i<n;i++){
        int x=read(),y=read(),z=read();
        insert(x,y,z);
        l=min(l,1.0*z);
        r=max(r,1.0*z);
    }
    root=0,Max=inf;
    Get_root(1,0,n);
    Frt(root);
    while (l<=r){
        double mid=(l+r)/2;
        if (check(mid)) l=mid+unit;
        else    r=mid-unit;
    }
    printf("%.3lf\n",r);
    return 0;
}

转载于:https://www.cnblogs.com/Wolfycz/p/10214917.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值