BZOJ4738 汽水

【题意】

求树上的一条路径,边权的平均值最接近k

【分析】

简单写一下式子,先要用分数规划二分答案

首先我们给所有的边权减去k,简化计算

我选择的方法是在每次分治的时候将所有的子树节点和自己加入按照边权和排序,然后分和大于0和小于0两部分计算,二分答案,>0/<0的有任意一个满足即可

【代码】

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+5;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int inf=0x3f3f3f3f;
const ll linf=1e18;
int n;
ll k,ans;
int head[maxn],tot,size;
struct edge
{
    int to,nxt;
    ll v;
}e[maxn<<1];
void add(int x,int y,ll z)
{
    e[++tot].to=y; e[tot].nxt=head[x]; e[tot].v=z; head[x]=tot;
}
int root,siz[maxn],gsiz,vis[maxn];
void findrt(int u,int fa)
{
    siz[u]=1;
    int maxnum=0;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        findrt(to,u);
        siz[u]+=siz[to];
        maxnum=max(maxnum,siz[to]);
    }
    maxnum=max(maxnum,size-siz[u]);
    if(maxnum<gsiz)
    {
        gsiz=maxnum;
        root=u;
    }
}
struct point
{
    ll sum,dep,no;
}s[maxn];
int cnt,pos;
bool cmp(point a,point b)
{
    return a.sum<b.sum;
}
void dfs(int u,int fa,ll w,int dep,int no)
{
    s[++cnt]=(point){w,dep,no};
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        dfs(to,u,w+e[i].v,dep+1,no);
    }
}
PLL A,B;
#define mp make_pair
#define fi first
#define se second
void update(PLL c)
{
    if(c.fi>B.fi) return;
    if(c.fi<A.fi)
    {
        if(c.se!=A.se) B=A;
        A=c;
    }
    else if(c.se!=A.se) B=c; 
}
bool check1(ll k)
{
    A=B=mp(linf,0);
    for(int i=pos,j=pos-1;i<=cnt;i++)
    {
        while(j && s[i].sum+s[j].sum>=0) update(mp(s[j].sum-s[j].dep*k,s[j].no)),j--;
        if((A.se==s[i].no?B.fi:A.fi)<k*s[i].dep-s[i].sum) return 1;
        update(mp(s[i].sum-s[i].dep*k,s[i].no));
    }
    return 0;
}
bool check2(ll k)
{
    A=B=mp(linf,0);
    for(int i=pos-1,j=pos;i;i--)
    {
        while(j<=cnt && s[i].sum+s[j].sum<0) update(mp(-s[j].sum+s[j].dep*k,s[j].no)),j++;
        if((A.se==s[i].no?B.fi:A.fi)<-k*s[i].dep+s[i].sum) return 1;
        update(mp(-s[i].sum+s[i].dep*k,s[i].no));
    }
    return 0;    
}
void divide(int u)
{
    vis[u]=1; s[cnt=1]=(point){0,0,0};
    for(int i=head[u];i;i=e[i].nxt)
        if(!vis[e[i].to])
            dfs(e[i].to,u,e[i].v,1,e[i].to);
    sort(s+1,s+cnt+1,cmp);
    for(pos=1;pos<=cnt && s[pos].sum<0;pos++);
    ll l=1,r=ans-1;
    while(l<=r)
    {
        ll mid=l+r>>1;
        if(check1(mid) || check2(-mid)) r=mid-1;
        else l=mid+1;
    }
    ans=min(ans,l);
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        size=siz[u]; gsiz=inf;
        findrt(to,0);
        divide(root);
    }
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%d%lld",&n,&k);
    int x,y; ll z;
    ans=linf;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%lld",&x,&y,&z);
        add(x,y,z-k); add(y,x,z-k);
        ans=min(ans,abs(z-k)+1);
    }
    size=n; gsiz=inf; 
    findrt(1,0);
    divide(1);
    printf("%lld\n",ans-1);
    return 0;
}

 

posted @ 2021-05-17 21:09  andyc_03  阅读(38)  评论(0)    收藏  举报