UOJ #276「清华集训2016」汽水

为什么你们常数都这么小啊

UOJ #276

题意:在树上找一条链使得|边权平均值$ -k$|尽量小,$ n<=5e4$


$ Solution:$

首先二分答案$ ans$,即我们需要找一条链使得边权平均值 $in [-ans,ans]$

我们分正负两半分开讨论

先假设平均值$ in (0,ans]$

将原树点分

统计过根的所有链

将这些链记录长度$len$,边数$sum$,所属子树标号$id$之后按长度排序

添加一条链$(0,0,0)$,则过某点的链一定是某两条不在同一子树的链拼接而成

两条链拼接合法当且仅当$ frac{len_i+len_j}{sum_i+sum_j} in (0,ans]$

化简得$ len_i+len_j>0且len_i-ans·sum_i+len_j-ans·sum_j leq 0$

我们记录后缀$ len_i-ans·sum_i$的最小值

然后两个指针扫扫就好了

注意存在不在同一子树的限制,我们需要同时记录不在同一子树的后缀最小值和次小值,这样才能合并

如果平均值$ in [-ans,0]$,也用同样的方法维护前缀最大值和次大值即可

注意各种边界的问题

时间复杂度:$ O(n log n log val)$


$ my code$

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector> 
#define M 100010
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x = 0; char zf = 1; char ch = getchar();
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('
');}
int i,j,k,m,n,x,y,z,cnt,all,Root,nowmin;
int F[M],L[M],N[M],a[M],size[M];ll c[M];
bool vis[M];
void add(int x,int y,ll z){
    a[++k]=y;c[k]=z;
    if(!F[x])F[x]=k;
    else N[L[x]]=k;
    L[x]=k; 
}
void getRoot(int x,int pre){
    size[x]=1;int Maxsize=0;
    for(rt i=F[x];i;i=N[i])if(a[i]!=pre&&!vis[a[i]]){
        getRoot(a[i],x);size[x]+=size[a[i]];
        Maxsize=max(Maxsize,size[a[i]]);
    }
    Maxsize=max(Maxsize,all-size[x]);
    if(Maxsize<nowmin)nowmin=Maxsize,Root=x;
}
int cs;
vector<int>e[50010];
void build(int x){
    int ls=all;vis[x]=1;
    for(rt i=F[x];i;i=N[i])if(!vis[a[i]]){
        all=(size[a[i]]>size[x]?ls-size[x]:size[a[i]]);
        nowmin=999999999;Root=233333;getRoot(a[i],x);
        e[x].push_back(Root);
        build(Root);
    }
}
struct chain{
    ll len;int sl,col;
    bool operator <(const chain s)const{
        return len<s.len;
    }
}q[50010];int t;
void Add(int col,int x,int pre,ll len,int sl){
    q[++t]={len,sl,col};
    for(rt i=F[x];i;i=N[i])if(a[i]!=pre&&!vis[a[i]])Add(col,a[i],x,len+c[i],sl+1);
}
int qzmax[50010][2],hzmin[50010][2],allsize=0;
bool check(int x,ll ans){//0...x
    q[t=1]={0,0,0};vis[x]=1;
    for(rt i=F[x];i;i=N[i])if(!vis[a[i]])Add(a[i],a[i],x,c[i],1);
    sort(q+1,q+t+1);
    qzmax[1][0]=1;qzmax[1][1]=0;
    for(rt i=2;i<=t;i++){
        int id0=qzmax[i-1][0],id1=qzmax[i-1][1];
        qzmax[i][0]=id0;qzmax[i][1]=id1;
        if(q[i].len+ans*q[i].sl>q[id0].len+ans*q[id0].sl){
            if(q[i].col!=q[id0].col)qzmax[i][1]=qzmax[i][0];
            qzmax[i][0]=i;
        }
        else if(q[i].len+ans*q[i].sl>q[id1].len+ans*q[id1].sl||!id1){
            if(q[i].col!=q[id0].col)qzmax[i][1]=i;
        }
    }
    hzmin[t][0]=t;hzmin[t][1]=0;
    for(rt i=t-1;i>=1;i--){
        int id0=hzmin[i+1][0],id1=hzmin[i+1][1];
        hzmin[i][0]=id0;hzmin[i][1]=id1;
        if(q[i].len-ans*q[i].sl<q[id0].len-ans*q[id0].sl){
            if(q[i].col!=q[id0].col)hzmin[i][1]=hzmin[i][0];
            hzmin[i][0]=i;
        }
        else if(q[i].len-ans*q[i].sl<q[id1].len-ans*q[id1].sl||!id1){
            if(q[i].col!=q[id0].col)hzmin[i][1]=i;
        }        
    }
    int R=t;
    for(rt i=1;i<=t;i++){
        while((q[i].len+q[R].len>0||R==i)&&R)R--;
        if(R==0)break;if(R>=i)continue;
        int maxv=qzmax[R][0];
        if(q[qzmax[R][0]].col==q[i].col)maxv=qzmax[R][1];
        if(maxv==0)continue;
        if(q[i].len+q[i].sl*ans+q[maxv].len+ans*q[maxv].sl>0)return 1;
    }
    int L=1;
    for(rt i=t;i>=1;i--){
        while((q[i].len+q[L].len<0||L==i)&&L<=t)L++;
        if(L>t)break;if(L<=i)continue;
        int minv=hzmin[L][0];
        if(q[hzmin[L][0]].col==q[i].col)minv=hzmin[L][1];
        if(!minv)continue;
        if(q[i].len-q[i].sl*ans+q[minv].len-ans*q[minv].sl<0)return 1;        
    }
    bool res=0;
    for(rt i=0;i<e[x].size();i++)if(!res)res|=check(e[x][i],ans);
    return res;
}
int main(){
    n=read();ll w=read();
    for(rt i=1;i<n;i++){
        x=read();y=read();ll z=read();
        add(x,y,z-w);
        add(y,x,z-w);
    }
    nowmin=999999999;all=n;getRoot(1,1);
    int troot=Root;build(Root);
    memset(vis,0,sizeof(vis));
    ll L=0,R=10000000000000;
    while(L<=R){
        memset(vis,0,sizeof(vis));
        ll mid=(L+R)/2;
        if(check(troot,mid))R=mid-1;
        else L=mid+1;
    }
    write(R);
    return 0;
}
原文地址:https://www.cnblogs.com/DreamlessDreams/p/10059572.html