UOJ #276「清华集训2016」汽水
为什么你们常数都这么小啊
题意:在树上找一条链使得|边权平均值$ -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('\n');} 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; }