CF150E Freezing with Style

題面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;
}
相關文章
相關標籤/搜索