• CF150E Freezing with Style


    题面

    英文题面

    题意:给定一颗带边权的树,求一条边数在 [L,R][L,R] 之间的路径,并使得路径上边权的中位数最大。输出一条可行路径的两个端点。有两个中位数时取较大的那个。(n leq 10^5)

    题解:对于中位数的题,常见的套路是二分答案(w),将小于(w)的数定为-1或0,将大于等于(w)的数定为1,然后就能方便地进行一些操作。那么对于本题,二分答案(w)后,check时我们就只需要找到是否存在一条路径,它的边权和(geq 0)

    考虑用点分治做。因为我们有(L,R)的限制,所以需要记一个(a_i)表示与分治中心距离为(i)的边权和的最大值。那么一个显然的做法就是大力搞棵线段树。但考虑到每个点在单次分治时需要查一次线段树,修改一次线段树,还要进行build操作,那么总复杂度就成了三个log,常数也很大,很难通过。考虑优化。

    我们在进入分治中心的一个儿子查询时,可以考虑换一种查询方法:记(b_i)表示当前这个儿子的最大值,定义与(a_i)类似。又因为对于每个(i),能对它产生贡献的是一段连续区间,且当(i)增大时,是个滑动窗口,那么就能用单调队列来优化这个过程。

    但考虑到对于每个儿子,单调队列都要产生(O(mxdep))的复杂度,这显然是不可接受的。所以,我们先将所有儿子按子树最大深度(mxdep)排序,这样,对于每个儿子(x),复杂度就是(O(mxdep_x)),加起来是(O(n))级别的。这样,点分治的总复杂度就是(O(nlogn))

    最好把点分树先建出来,以避免冗余的计算。

    时间复杂度:(O(nlog^2n))

    代码:又臭又长

    #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;
    }
    
  • 相关阅读:
    php 函数ignore_user_abort()
    关于VMAX中存储资源池(SRP)
    VMware Integrated OpenStack (VIO)简介
    云计算服务的三种类型(SaaS、PaaS、IaaS)
    vMware存储:SAN配置基础
    关于不同应用程序存储IO类型的描述
    (转)OpenFire源码学习之十:连接管理(上)
    (转)OpenFire源码学习之九:OF的缓存机制
    (转)OpenFire源码学习之八:MUC用户聊天室
    (转)OpenFire源码学习之七:组(用户群)与花名册(用户好友)
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/13296021.html
Copyright © 2020-2023  润新知