UOJ276 清华集训2016 汽水

Problem

UOJ

Solution

把所有边权都减去 k k k ,那么就要求平均边权的绝对值最小。

由于结果要向下取整,因此我们判定的符号应该是严格小于,即 − m i d < a n s < m i d -mid < ans < mid mid<ans<mid

考虑点分治,把一条路径表示为 ( A i , B i ) (A_i,B_i) (Ai,Bi),其中 A i A_i Ai 表示根到节点的边权和, B i B_i Bi 表示深度,那么我们如果要合并两条路径就需要

− m i d < A 1 + A 2 B 1 + B 2 < m i d -mid<\frac {A_1+A_2} {B_1+B_2}<mid mid<B1+B2A1+A2<mid

不难发现分母是大于 0 0 0 的,因此我们可以对分子的正负性进行讨论,当我们保证分子为正时, A 1 + A 2 < m i d B 1 + m i d B 2 A_1+A_2<midB_1+midB_2 A1+A2<midB1+midB2,那么 A 1 − m i d B 1 < m i d B 2 − A 2 A_1-midB_1<midB_2-A_2 A1midB1<midB2A2。对 A A A 排序后即可two-pointer,记录一个最小值即可,同时由于拼接的路径不能在一棵子树中,我们需要记录第二小的另一棵子树中的答案。分子为负时同理。

每次分治时可以把二分的上界不断更新,所以把二分写在点分治里面会稍微快一些,而且也不需要预处理什么鬼点分树了。

时间复杂度 O ( n log ⁡ n log ⁡ v ) O(n\log n\log v) O(nlognlogv)。注意 I N F INF INF 不要设小了。

Code

#include 
#include 
using namespace std;
typedef long long ll;
const int maxn=50010;
const ll INF=1e18;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{
	int v,nxt;ll w;
	data(const int _v=0,const ll _w=0ll,const int _nxt=0){v=_v;w=_w;nxt=_nxt;}
}edge[maxn<<1];
struct node{
	ll sum;int dep,anc;
	node(const ll _sum=0ll,const int _dep=0,const int _anc=0){sum=_sum;dep=_dep;anc=_anc;}
	bool operator < (const node &b)const{return sum<b.sum;}
}m1,m2,stk[maxn];
int n,p,top,head[maxn];
int rt,sn,mx[maxn],sz[maxn],vis[maxn];
ll m,ans=INF;
void insert(int u,int v,ll w)
{
	edge[++p]=data(v,w,head[u]);head[u]=p;
	edge[++p]=data(u,w,head[v]);head[v]=p;
}
void getrt(int x,int pre)
{
	sz[x]=1;mx[x]=0;
	for(int i=head[x];i;i=edge[i].nxt)
	  if(!vis[edge[i].v]&&edge[i].v^pre)
	  {
	  	getrt(edge[i].v,x);
	  	sz[x]+=sz[edge[i].v];
	  	getmax(mx[x],sz[edge[i].v]);
	  }
	getmax(mx[x],sn-sz[x]);
	if(mx[x]<mx[rt]) rt=x;
}
void getdis(int x,int pre,ll sum,int dep,int anc)
{
	stk[++top]=node(sum,dep,anc);
	for(int i=head[x];i;i=edge[i].nxt)
	  if(!vis[edge[i].v]&&edge[i].v^pre)
	  	getdis(edge[i].v,x,sum+edge[i].w,dep+1,anc);
}
void input()
{
	int u,v;ll w;
	read(n);read(m);
	for(int i=1;i<n;i++)
	{
		read(u);read(v);read(w);
		insert(u,v,w-m);
		getmin(ans,abs(w-m));
	}
	mx[0]=0x3f3f3f3f;rt=0;sn=n;
	getrt(1,1);
}
void update(node now,ll mid)
{
	if(now.sum<m1.sum)
	{
		if(now.anc==m1.anc){m1.sum=now.sum;return ;}
		m2=m1;m1=now;return ;
	}
	if(now.sum<m2.sum&&now.anc!=m1.anc) m2=now;
}
int check1(ll mid)
{
	int p=top;
	m1=m2=node(INF,-1,-1);
	for(int i=1;i<=top;i++)
	{
		for(;p&&stk[p].sum+stk[i].sum>=0;--p)
		  update(node(stk[p].sum-stk[p].dep*mid,-1,stk[p].anc),mid);
		if(stk[i].dep*mid-stk[i].sum>(stk[i].anc==m1.anc?m2.sum:m1.sum))
		  return 1;
	}
	return 0;
}
int check2(ll mid)
{
	int p=1;
	m1=m2=node(INF,-1,-1);
	for(int i=top;i;i--)
	{
		for(;p<=top&&stk[p].sum+stk[i].sum<0;++p)
		  update(node(-stk[p].sum-stk[p].dep*mid,-1,stk[p].anc),mid);
		if(stk[i].dep*mid+stk[i].sum>(stk[i].anc==m1.anc?m2.sum:m1.sum))
		  return 1;
	}
	return 0;
}
void dfs(int x)
{
	vis[x]=1;getrt(x,0);
	stk[top=1]=node(0,0,x);
	for(int i=head[x];i;i=edge[i].nxt)
	  if(!vis[edge[i].v])
	    getdis(edge[i].v,x,edge[i].w,1,edge[i].v);
	sort(stk+1,stk+top+1);
	ll L=1,R=ans,mid;
	while(L<=R)
	{
		mid=(L+R)>>1;
		if(check1(mid)||check2(mid)) ans=mid-1,R=mid-1;//ans
		else L=mid+1;
	}
	for(int i=head[x];i;i=edge[i].nxt)
	  if(!vis[edge[i].v])
	  {
	  	rt=0;sn=sz[edge[i].v];
	  	getrt(edge[i].v,x);
	  	dfs(rt);
	  }
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	input();
	dfs(rt);
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(二分答案,点分治,UOJ)