zoukankan      html  css  js  c++  java
  • 题解「CF1073F Choosing Two Paths」

    有点意思又偏向套路的结论题。

    我们发现,最终答案一定是类似于一条路径 (mathrm{Path(u o v)}) 上延伸出两条路径 (mathrm{Path(x' o x),Path(y' o y)}) 这种形式,其中 (x',y') 都在路径 (u o v) 上。而我们要先让 (mathrm{dis(x',y')}) 最大(即相交部分最大),其次令路径 (mathrm{Path(u o v),Path(x' o x),Path(y' o y)}) 的长度之和最大。

    可以发现 (mathrm{Path(u o v)}) 这条路径,可以使用类似直径的两次 ( ext{dfs}) 求出。而一旦这条路径确定,另外两条路径的 (x',y') 是确定的,对应求出 (x',y') 子树中的叶子节点 (x,y) 即可。答案的链对就为 (mathrm{Path(u o v),Path(x o y)})

    但是如果 (u o v) 不满足一些条件,会导致对应求出的 (x,y) 形成的交集不是最大的,或者路径长度总和不是最大的(我在这个地方反复 ( ext{Wrong Answer on test 72/57/45}),不得不说 ( ext{Codeforces}) 的数据还是挺强的

    为了解决这个问题,我们在两遍 ( ext{dfs}) 的时候,按照优先级更新值即可。先考虑相交部分最大,其次考虑路径长度之和最大。由于需要求路径长度之和,我们需要预处理出 (x) 子树内最大距离和次大距离,这是一个套路就不细表了。

    那么,我们就可以在 (O(n)) 的时间复杂度内解决这个问题。代码写的有点凌乱随便看看就好(我才不会告诉你是我故意的呢~

    #include<cstdio>
    int pos=0,val=0,val2=0,cnt=0,mx,mn,rex,rey;
    int du[200005],h[200005],to[400005],ver[400005];
    int dis[200005],tag[200005],pre[200005],s[200005],leaf[200005],sec[200005];
    inline int read() {
    	register int x=0,f=1;register char s=getchar();
    	while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
    	while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    	return x*f;
    }
    inline void swap(int &x,int &y) {int tmp=y;y=x;x=tmp;}
    inline void add(int x,int y) {to[++cnt]=y;ver[cnt]=h[x];h[x]=cnt;}
    inline void dfs2(int x) { 
    	du[x]=0;
    	if(dis[mx]-dis[mn]>val) pos=x,val=dis[mx]-dis[mn],val2=dis[leaf[mn]]+dis[leaf[mx]]+dis[x]; else if(val==dis[mx]-dis[mn]&&dis[leaf[mn]]+dis[leaf[mx]]+dis[x]>val2) pos=x,val2=dis[leaf[mn]]+dis[leaf[mx]]+dis[x];
    	for(register int i=h[x],y;i;i=ver[i]) {if(pre[x]==(y=to[i])) continue; ++du[x];}
    	for(register int i=h[x],y;i;i=ver[i]) {
    		if(pre[x]==(y=to[i])) continue; 
    		pre[y]=x; dis[y]=dis[x]+1; int premx=mx,premn=mn; 
    		int flag=0; if(leaf[y]==leaf[x]) {flag=1;swap(leaf[x],sec[x]);} 
    		if(du[x]>1) {
    			if(dis[x]>dis[mx]) mx=x;
    			else if(dis[x]==dis[mx]&&dis[leaf[x]]>dis[leaf[mx]]) mx=x;
    			if(dis[x]<dis[mn]) mn=x;
    			else if(dis[x]==dis[mn]&&dis[leaf[x]]>dis[leaf[mn]]) mn=x; 
    		} 
    		dfs2(y); mx=premx; mn=premn;
    		if(flag) swap(leaf[x],sec[x]);
    	}
    }
    inline void dfs3(int x) {
    	leaf[x]=x; 
    	for(register int i=h[x],y;i;i=ver[i]) {
    		if(pre[x]==(y=to[i])||tag[y]) continue; pre[y]=x; dfs3(y); 
    		if(dis[leaf[x]]<dis[leaf[y]]) {sec[x]=leaf[x];leaf[x]=leaf[y];}
    		else if(dis[sec[x]]<dis[leaf[y]]) {sec[x]=leaf[y];}
    	}
    }
    int main() {
    	int n=read(),top=0,st,ed; dis[0]=mx=0; dis[n+1]=mn=n+1;
    	for(register int i=1;i<n;++i) {int x=read(),y=read();add(x,y);add(y,x);}
    	dis[1]=pos=val=0; dfs2(1); st=pos; pre[st]=-1; dis[st]=pos=val=0; dfs3(st); mx=0,mn=n+1; dfs2(st); ed=pos;
    	int cur=ed,rex,rey; while(cur!=-1) {s[++top]=cur; tag[cur]=1; cur=pre[cur];} mx=0,mn=n+1;
    	for(register int i=top-1;i>=2;--i) {
    		int x=s[i];
    		for(register int k=h[x],y;k;k=ver[k]) if(!tag[y=to[k]]) mx=(dis[x]>dis[mx]? x:mx),mn=(dis[x]<dis[mn]? x:mn); 
    	}
    	dfs3(mn); rex=leaf[mn]; dfs3(mx); rey=leaf[mx];
    	printf("%d %d
    %d %d
    ",st,ed,rex,rey);
    	return 0;
    }
    
  • 相关阅读:
    php之工厂模式
    PHP 给GIF 缩略图实例代码
    WGS84 Mercator project转换的算法(C#)
    .net Enum用法
    Google Maps API编程资源大全
    结对作业
    使用Lightbox制作照片条
    使用css3中transition的页面切换
    使用css3中transition的页面切换(继续创新版)
    iframe中父窗口获取子窗口的元素
  • 原文地址:https://www.cnblogs.com/tommy0103/p/13831837.html
Copyright © 2011-2022 走看看