• UOJ#276. 【清华集训2016】汽水 二分答案 点分治


    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ276.html

    题解

    首先,读入的时候就将所有的 $w_i$ 减掉 $k$ 。

    于是我们要求的就是平均值最接近 0 的。

    直接点分治,然后得到一些一端为当前点分中心的路径,设 $a,b$ 为其中两条路径,设 $v_a,v_b$ 为路径的边权和,$t_a,t_b$ 为路径的边数。

    二分一个答案,假设差别**小于** $A$。由于题目要求的是下取整,所以我们为了方便,设的是**小于** $A$ ,这样做,最终只需要把答案减一就好了。

    那么,如果合并路径 $a,b$ 可以满足条件,那么就会满足:

    $$left|cfrac{v_a+v_b}{t_a+t_b} ight|<A\|v_a+v_b|<A(t_a+t_b)\=egin{cases}v_a-At_a+v_b-At_b<0 (v_a+v_bgeq 0)\v_a+At_a+v_b+At_b>0 (v_a+v_b<0)end{cases}$$

    也就是说,我们只需要对于正的 $v_a$ 和负的 $v_a$ 分开考虑,在保证取到右侧条件的基础上,维护一下最大最小值之类的东西就好了。

    具体还是看代码吧。

    代码

    #include <bits/stdc++.h>
    #define mp make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return x;
    }
    const int N=50005;
    const LL INF=1LL<<60;
    int n;
    LL K,ans=INF;
    vector <pair <int,LL> > e[N];
    int vis[N],size[N],Maxsize[N],root,Size;
    void get_root(int x,int pre){
    	size[x]=1,Maxsize[x]=0;
    	for (auto E : e[x])
    		if (E.fi!=pre&&!vis[E.fi]){
    			get_root(E.fi,x);
    			size[x]+=size[E.fi];
    			Maxsize[x]=max(Maxsize[x],size[E.fi]);
    		}
    	Maxsize[x]=max(Maxsize[x],Size-size[x]);
    	if (Maxsize[x]<Maxsize[root])
    		root=x;
    }
    struct Node{
    	int t,id;
    	LL v;
    	Node(int _t=0,LL _v=0,int _id=0){
    		t=_t,v=_v,id=_id;
    	}
    	friend bool operator < (Node a,Node b){
    		return a.v<b.v;
    	}
    }posi[N],nega[N];
    int pc,nc;
    void dfs(int x,int pre,int cnt,LL S,int ID){
    	if (S>=0)
    		posi[++pc]=Node{cnt,S,ID};
    	else
    		nega[++nc]=Node{cnt,S,ID};
    	for (auto E : e[x])
    		if (E.fi!=pre&&!vis[E.fi])
    			dfs(E.fi,x,cnt+1,S+E.se,ID);
    }
    pair <int,LL> _1,_2;
    void ckMax(pair <int,LL> _3){
    	if (_3.se>_1.se){
    		if (_3.fi!=_1.fi)
    			_2=_1;
    		_1=_3;
    	}
    	else if (_3.se>_2.se&&_3.fi!=_1.fi)
    		_2=_3;
    }
    void ckMin(pair <int,LL> _3){
    	if (_3.se<_1.se){
    		if (_3.fi!=_1.fi)
    			_2=_1;
    		_1=_3;
    	}
    	else if (_3.se<_2.se&&_3.fi!=_1.fi)
    		_2=_3;
    }
    int check(LL x){
    	_1=_2=mp(0,INF);
    	for (int i=1,j=nc;i<=pc;i++){
    		while (j>0&&posi[i].v+nega[j].v>=0)
    			ckMin(mp(nega[j].id,nega[j].v-x*nega[j].t)),j--;
    		if (posi[i].v-x*posi[i].t+(posi[i].id==_1.fi?_2.se:_1.se)<0)
    			return 1;
    		ckMin(mp(posi[i].id,posi[i].v-x*posi[i].t));
    	}
    	_1=_2=mp(0,-INF);
    	for (int i=pc,j=1;i>=1;i--){
    		while (j<=nc&&posi[i].v+nega[j].v<0)
    			ckMax(mp(nega[j].id,nega[j].v+x*nega[j].t)),j++;
    		if (posi[i].v+x*posi[i].t+(posi[i].id==_1.fi?_2.se:_1.se)>0)
    			return 1;
    		ckMin(mp(posi[i].id,posi[i].v+x*posi[i].t));
    	}
    	return 0;
    }
    void solve(int x){
    	Maxsize[0]=n+1;
    	root=pc=nc=0;
    	get_root(x,0);
    	vis[x=root]=1;
    	posi[++pc]=Node{0,0,x};
    	for (auto E : e[x])
    		if (!vis[E.fi])
    			dfs(E.fi,x,1,E.se,E.fi);
    	sort(posi+1,posi+pc+1);
    	sort(nega+1,nega+nc+1);
    	LL L=1,R=ans-1,mid;
    	while (L<=R){
    		mid=(L+R)>>1;
    		if (check(mid))
    			R=mid-1;
    		else
    			L=mid+1;
    	}
    	ans=min(ans,L);
    	for (auto E : e[x])
    		if (!vis[E.fi])
    			Size=size[E.fi],solve(E.fi);
    }
    int main(){
    	Size=n=read(),K=read();
    	for (int i=1;i<n;i++){
    		int a=read(),b=read();
    		LL c=read()-K;
    		ans=min(ans,abs(c)+1);
    		e[a].push_back(mp(b,c));
    		e[b].push_back(mp(a,c));
    	}
    	solve(1);
    	printf("%lld
    ",ans-1);
    	return 0;
    }
    

      

  • 相关阅读:
    hoj 1061 排列树问题
    [译稿]Google的9条创新原则(转)
    vs2008 Sys未定义比较完整的解决方案
    js右下角升起小窗口脚本示例
    扩展了flash8里array的方法
    js 滚动、切换代码的搜集
    prototype 1.3.1 跟 ajax冲突!!!莫名其妙!
    用AS删除Flash中输入文本开始和结尾的空格【转载】
    Flash右键触发与屏蔽
    实现Flash跨域访问
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ276.html
Copyright © 2020-2023  润新知