题面c++
英文题面git
题意:给定一颗带边权的树,求一条边数在 [L,R][L,R] 之间的路径,并使得路径上边权的中位数最大。输出一条可行路径的两个端点。有两个中位数时取较大的那个。\(n \leq 10^5\)。ide
题解:对于中位数的题,常见的套路是二分答案\(w\),将小于\(w\)的数定为-1或0,将大于等于\(w\)的数定为1,而后就能方便地进行一些操做。那么对于本题,二分答案\(w\)后,check时咱们就只须要找到是否存在一条路径,它的边权和\(\geq 0\)。优化
考虑用点分治作。由于咱们有\(L,R\)的限制,因此须要记一个\(a_i\)表示与分治中心距离为\(i\)的边权和的最大值。那么一个显然的作法就是大力搞棵线段树。但考虑到每一个点在单次分治时须要查一次线段树,修改一次线段树,还要进行build操做,那么总复杂度就成了三个log,常数也很大,很难经过。考虑优化。ui
咱们在进入分治中心的一个儿子查询时,能够考虑换一种查询方法:记\(b_i\)表示当前这个儿子的最大值,定义与\(a_i\)相似。又由于对于每一个\(i\),能对它产生贡献的是一段连续区间,且当\(i\)增大时,是个滑动窗口,那么就能用单调队列来优化这个过程。spa
但考虑到对于每一个儿子,单调队列都要产生\(O(mxdep)\)的复杂度,这显然是不可接受的。因此,咱们先将全部儿子按子树最大深度\(mxdep\)排序,这样,对于每一个儿子\(x\),复杂度就是\(O(mxdep_x)\),加起来是\(O(n)\)级别的。这样,点分治的总复杂度就是\(O(nlogn)\)。code
最好把点分树先建出来,以免冗余的计算。排序
时间复杂度:\(O(nlog^2n)\)。队列
代码:又臭又长get
#include<bits/stdc++.h> using namespace std; #define re register int #define F(x,y,z) for(re x=y;x<=z;x++) #define FOR(x,y,z) for(re x=y;x>=z;x--) typedef long long ll; #define I inline void #define IN inline int #define C(x,y) memset(x,y,sizeof(x)) #define STS system("pause") template<class D>I read(D &res){ res=0;register D g=1;register char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')g=-1; ch=getchar(); } while(isdigit(ch)){ res=(res<<3)+(res<<1)+(ch^48); ch=getchar(); } res*=g; } const int INF=1e9+7; vector<int>t[101000]; struct E{ int to,nt,w,v; }e[202000]; #define T e[k].to int n,m,rot,L,R,M,sn,head[101000],tot,X,Y,W; int siz[101000],vis[101000],dep[101000],mx[101000],val[101000],a[101000],pa[101000],b[101000],pb[101000],q[101000],hed,tal,N,maxi,root; int A,B; vector<int>son; I add(int x,int y,int w){ e[++tot].to=y;e[tot].nt=head[x];head[x]=tot;e[tot].w=w; } I findroot(int x,int fa){ siz[x]=1;mx[x]=0; for(re k=head[x];k!=-1;k=e[k].nt){ if(T==fa||vis[T])continue; findroot(T,x); siz[x]+=siz[T];mx[x]=max(mx[x],siz[T]); } mx[x]=max(mx[x],N-siz[x]); if(mx[x]<maxi)maxi=mx[x],root=x; } I D_1(int x,int fa){ siz[x]=1; for(re k=head[x];k!=-1;k=e[k].nt){ if(T==fa||vis[T])continue; D_1(T,x);siz[x]+=siz[T]; } } I divided(int x,int fa){ if(fa)t[fa].emplace_back(x); //cout<<"!"<<fa<<" "<<x<<endl; vis[x]=1;D_1(x,0); for(re k=head[x];k!=-1;k=e[k].nt){ if(T==fa||vis[T])continue; N=siz[T];maxi=INF;findroot(T,x); divided(root,x); } } I D_2(int x,int fa,int depth){ dep[x]=depth;mx[x]=depth; for(re k=head[x];k!=-1;k=e[k].nt){ if(T==fa||vis[T])continue; val[T]=val[x]+e[k].v;D_2(T,x,depth+1); mx[x]=max(mx[x],mx[T]); } } inline bool bbb(int x,int y){return mx[x]<mx[y];} I D_3(int x,int fa){ if(val[x]>b[dep[x]])b[dep[x]]=val[x],pb[dep[x]]=x; for(re k=head[x];k!=-1;k=e[k].nt){ if(T==fa||vis[T])continue; D_3(T,x); } } I conquer(int x){ if(sn)return; // cout<<"Conq:"<<x<<endl; son.clear();val[x]=0;D_2(x,0,0); for(re k=head[x];k!=-1;k=e[k].nt){ if(vis[T])continue;son.emplace_back(T); } sort(son.begin(),son.end(),bbb); F(i,0,mx[x])a[i]=b[i]=-INF,pa[i]=pb[i]=0; maxi=0;a[0]=0;pa[0]=x; for(auto d:son){ // cout<<"Son "<<d<<":"<<endl; F(i,1,mx[d])b[i]=-INF,pb[i]=0; D_3(d,x); // F(i,1,mx[d])cout<<b[i]<<" "<<pb[i]<<endl; hed=1;tal=0; FOR(i,min(R-1,maxi),L-1){ if(a[i]==-INF)continue; while(hed<=tal&&a[q[tal]]<=a[i])tal--; q[++tal]=i; } if(hed<=tal&&b[1]+a[q[hed]]>=0)sn=1,A=pa[q[hed]],B=pb[1]; F(i,2,mx[d]){ while(hed<=tal&&q[hed]+i>R)hed++; if(L-i<=maxi&&L>=i&&a[L-i]>-INF){ while(hed<=tal&&a[q[tal]]<=a[L-i])tal--; q[++tal]=L-i; } if(hed<=tal&&b[i]+a[q[hed]]>=0)sn=1,A=pa[q[hed]],B=pb[i]; } F(i,1,mx[d]){ if(a[i]<b[i])a[i]=b[i],pa[i]=pb[i]; } maxi=mx[d]; } vis[x]=1; for(auto d:t[x])conquer(d); } IN ck(int lim){ F(i,1,n)for(re k=head[i];k!=-1;k=e[k].nt){ if(e[k].w<lim)e[k].v=-1;else e[k].v=1; } F(i,1,n)vis[i]=0; sn=0;conquer(rot);return sn; } I dichotomize(int l,int r){ if(l==r)return ck(l),void(); re mid=(l+r+1)>>1; if(ck(mid))dichotomize(mid,r); else dichotomize(l,mid-1); //,cout<<"!"<<l<<endl } int main(){ read(n);read(L);read(R);C(head,-1);tot=-1; F(i,1,n-1){ read(X);read(Y);read(W);add(X,Y,W);add(Y,X,W);M=max(M,W); } N=n;maxi=INF;findroot(1,0);rot=root; divided(root,0); dichotomize(0,M); printf("%d %d",A,B); return 0; }