uoj#276. 【清华集训2016】汽水(分数规划+点分治)

传送门

没想到点分治那一层……

首先不难发现这是个分数规划,先把所有的边长减去\(k\),二分答案,设为\(mid\),就是要求路径平均值\(ans\in[-mid,mid]\)

先来考虑\(ans\in[0,mid]\)的的情况。我们考虑点分治,记下所有从根节点延伸下去的链,长度记为\(len\),边数为\(dep\),属于那一颗子树为\(bl\),先添加一条链\((0,0,0)\),那么两条链合法当且仅当\(\frac{len_i+len_j}{dep_i+dep_j}\in [0,mid]\),即有\(len_i+len_j\geq 0\)\(len_i-mid\times dep_i+len_j-mid\times dep_j\leq 0\),那么把所有的链按长度排序,当从左到右扫描\(j\)的时候,可行的\(j\)的左端点一定是不断左移的,所以可以拿两个指针扫。不过要注意两条链不能在同一棵子树内,所以当\(j\)不断左移时,要记录属于两个不同子树的最大值才行

然后这里有一个小\(trick\),按上面双指针移的话可能会出现某一个最大值就是\(i\)的情况,要分类讨论很麻烦。我们可以把链按照长度的正负分为两类,那么上面那种情况可以把指针在长度为正的里面左移,在长度为负的里面右移,那么长度为负的里面的每一条链都可以用来更新最大值,而长度为正的只要在判断完之后再更新就可以了

\(ans\in[-mid,0]\)同理,不多讲了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fi first
#define se second
#define inf 1e18
#define pi pair<ll,int>
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*pp=buf,*ppp=buf;
inline char getc(){return pp==ppp&&(ppp=(pp=buf)+fread(buf,1,1<<21,stdin),pp==ppp)?EOF:*pp++;}
ll read(){
    R ll res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=1e5+5;
struct eg{int v,nx;ll w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
struct node{
    int dep;ll dis;int bl;
    node(int dep=0,ll dis=0,int bl=0):dep(dep),dis(dis),bl(bl){}
    inline bool operator <(const node &b)const{return dis<b.dis;}
}p1[N],p2[N];ll k,w,ans=inf;pi A,B;
int sz[N],son[N],vis[N];
int n,rt,size,u,v,t1,t2;
void findrt(int u,int fa){
    sz[u]=1,son[u]=0;
    go(u)if(v!=fa&&!vis[v]){
        findrt(v,u),sz[u]+=sz[v];
        son[u]=max(son[u],sz[v]);
    }son[u]=max(son[u],size-sz[u]);
    if(son[u]<son[rt])rt=u;
}
void dfs(int u,int fa,int dep,ll dis,int bl){
    (dis>=0?(p1[++t1]):(p2[++t2]))=node(dep,dis,bl);
    go(u)if(v!=fa&&!vis[v])dfs(v,u,dep+1,dis+e[i].w,bl);
}
void cmin(pi x){
    if(x.fi<A.fi){
        if(x.se!=A.se)B=A;
        A=x;
    }else if(x.fi<B.fi&&x.se!=A.se)B=x;
}
void cmax(pi x){
    if(x.fi>A.fi){
        if(x.se!=A.se)B=A;
        A=x;
    }else if(x.fi>B.fi&&x.se!=A.se)B=x;
}
bool ck(ll val){
    A=B=pi(inf,0);
    for(R int i=1,j=t2;i<=t1;++i){
        while(p1[i].dis+p2[j].dis>=0&&j){
            cmin(pi(p2[j].dis-p2[j].dep*val,p2[j].bl)),--j;
        }if((A.se==p1[i].bl?B.fi:A.fi)+p1[i].dis-p1[i].dep*val<0)return 1;
        cmin(pi(p1[i].dis-p1[i].dep*val,p1[i].bl));
    }
    A=B=pi(-inf,0);
    for(R int i=t2,j=1;i;--i){
        while(p2[i].dis+p1[j].dis<0&&j<=t1){
            cmax(pi(p1[j].dis+p1[j].dep*val,p1[j].bl)),++j;
        }if((A.se==p2[i].bl?B.fi:A.fi)+p2[i].dis+p2[i].dep*val>0)return 1;
        cmax(pi(p2[i].dis+p2[i].dep*val,p2[i].bl));
    }
    return 0;
}
void solve(int u){
    vis[u]=1;int dsize=size;
    t1=t2=0;p1[++t1]=node(0,0,u);
    go(u)if(!vis[v])dfs(v,u,1,e[i].w,v);
    sort(p1+1,p1+t1+1),sort(p2+1,p2+t2+1);
    ll l=1,r=ans-1,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(ck(mid))r=mid-1;
        else l=mid+1;
    }ans=min(ans,l);
    go(u)if(!vis[v]){
        size=(sz[u]>sz[v]?sz[v]:dsize-sz[u]);
        rt=0,findrt(v,0),solve(rt);
    }
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read(),k=read();
    fp(i,1,n-1)u=read(),v=read(),w=read()-k,add(u,v,w),add(v,u,w),ans=min(ans,abs(w)+1);
    son[0]=n+1,rt=0,size=n;
    findrt(1,0),solve(rt);
    printf("%lld\n",ans-1);
    return 0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10242325.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值