loj2478林克卡特树

听说这题要用到一个叫做“带权二分”的黑科技(名称来自于APIO2018讲课课件),那就让我来体验一下。
写了一发,感觉概念还好懂,但特别容易挂。
有几个要点:
1.要对输入数据加以随机扰动,不然会被三点共线的数据卡。
2.如果上次答案和这次答案的差很小,则直接输出这次答案,不然会被卡精度。
3.其实可以把权值和方案封装成一个struct,这样写起来会方便一点。
然后就抢下了洛谷运行时间rk1?真爽。
达成成就->做完九省联考Day2所有题目

// luogu-judger-enable-o2
#include<bits/stdc++.h> 
typedef unsigned int ui;
inline ui R(){
    static ui seed=time(0);
    return seed^=seed>>5,seed^=seed<<17,seed^=seed>>13;
}
char ibuf[1<<25],*ih=ibuf,obuf[1<<25],*oh=obuf;
inline void read(int&x){
    int f=1;for(;!isdigit(*ih);++ih)if(*ih=='-')f=-1;
    for(x=0;isdigit(*ih);x=x*10+*ih++-48);x*=f;
}
const int N=3000005;
struct edge{int to,next;double w;}e[N<<1];
int da[N],id[N],xb,hh[N],i,k,n,x,y,v,a[N],b[N],c[N];
double ef[N],f[N],g[N],h[N];
void dfs(int x,int fa){
    id[x]=++xb;da[xb]=id[fa];
    for(int i=hh[x];i;i=e[i].next)if(e[i].to!=fa)ef[xb+1]=e[i].w,dfs(e[i].to,x);
}
int main(){
    fread(ibuf,1,1<<25,stdin);
    read(n);read(k);
    for(i=1;i<n;++i){
        read(x);read(y);read(v);
        double z=(((long long)R()<<30|R())%19260817-9630408)/963040800000000.0;
        e[++xb]=(edge){y,hh[x],v+z};hh[x]=xb;
        e[++xb]=(edge){x,hh[y],v+z};hh[y]=xb;
    }
    xb=0;dfs(1,0);
    double r=1e12,l=-1e12,m,lst;
    for(;;){
        m=(l+r)/2;
        memset(f+1,0,n<<3);memset(g+1,0,n<<3);memset(h+1,0,n<<3);
        memset(a+1,0,n<<2);for(i=1;i<=n;++i)b[i]=c[i]=1;
        double z=-1e18;int su;
        for(i=n;i;--i){
            double x,of=f[da[i]],og=g[da[i]],oh=h[da[i]],u;
            int oa=a[da[i]],ob=b[da[i]],oc=c[da[i]],v,y;
            if(f[i]>h[i]-m && f[i]>g[i]-m)u=f[i],v=a[i];
                else if(h[i]-m>g[i]-m)u=h[i]-m,v=c[i];
                        else u=g[i]-m,v=b[i];
            if(of+u>f[da[i]])f[da[i]]=of+u,a[da[i]]=oa+v;
            x=g[i]+ef[i];y=b[i];
            if(of+x>g[da[i]])g[da[i]]=of+x,b[da[i]]=oa+y;
            if(og+u>g[da[i]])g[da[i]]=og+u,b[da[i]]=ob+v;
            if(oh+u>h[da[i]])h[da[i]]=oh+u,c[da[i]]=oc+v;
            if(ob && og+x>h[da[i]])h[da[i]]=og+x,c[da[i]]=ob+y-1;
            if(f[i]>z)z=f[i],su=a[i];
            if(g[i]-m>z)z=g[i]-m,su=b[i];
            if(h[i]-m>z)z=h[i]-m,su=c[i];
        }
        if(su==k+1 || (su && fabs(lst-z)<0.1)){
            printf("%.0f\n",z+m*(k+1));
            break;
        }
        if((z==0 && su==0) || (su && su<k+1))r=m;else l=m;
        lst=z;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值