zoukankan      html  css  js  c++  java
  • 【题解】Acwing392 会合

    Acwing392 会合

    ( ext{Solution:})

    简单题……确实

    这是一个基环树森林上找两点换上最短移动方案的题。慢慢考虑其性质。

    • 每个点有且仅有一条出边

    这意味着这一定是 内向基环树森林

    还有更重要的意义:环有顺序

    • 求两个点移动到一个公共点上

    首先考虑两个点共同在一棵树内的情况,也就是它们两个不跨环。

    这个时候我们发现,由于树有方向,所以它们只能向上跳,这也意味着答案就是 (LCA) 与它们深度的差。

    那么两个点不在同一棵基环树中呢?直接输出 -1 -1 ,这也是唯一的无解情况。

    那么,如果它们需要跨环,我们就先让他们跳到环上考虑。

    引理:环上点移动最优情况下一定只会移动一个点。

    证明:题目中要求了使 ((x,y)) 的最大值最小值均最小,意味着我们不能做多余的移动。而两个点如果同时移动显然会无故增加次数,因为环是单向的。

    那么剩下就是考虑如何计算并分类了。

    关于计算环上的跳跃,考虑 按照顺序 来对每个点进行标号,这样就可以直接做差得到距离了。注意有两个距离,一个是正向直接跳,另一个是让另一个点绕一圈跳回来。

    然后按照题目所说分类讨论即可。但是有一些情况需要注意:

    1. 环是有方向的,所以在找环的时候 必须 用有向图来找。

    2. 打标记需要全部打上,所以这个时候我们选择用一张无向图来做。

    3. 会出现自环什么的情况,但是自然做就不会错。

    4. 有向图找环不需要判父亲!!!!

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+10;
    int head[N],tot,n,f[N][20],q,H[N],Tot;
    struct E{int nxt,to;}e[N<<1],edge[N<<1];
    inline int Max(int x,int y){
    	return x>y?x:y;
    }
    inline int Min(int x,int y){
    	return x<y?x:y;
    } 
    inline void link(int u,int v){
    	e[++tot]=(E){head[u],v};
    	head[u]=tot;
    }
    inline void Link(int u,int v){
    	edge[++Tot]=(E){H[u],v};
    	H[u]=Tot;
    }
    vector<int>Cir[N];
    int num,st[N],top,dep[N],vis[N];
    int to[N],Tg[N],trg[N],rk[N],Vis[N],ts[N]; 
    void Get(int node,int x){
    	int i;
    	for(i=top;i&&st[i]!=x;--i)Cir[node].push_back(st[i]),st[i]=0;
    	Cir[node].push_back(x);st[i]=0; 
    }
    void Find_Circle(int x,int fa,int node,int tg){
    	vis[x]=1;st[++top]=x;Tg[x]=tg;
    	for(int i=head[x];i;i=e[i].nxt){
    		int j=e[i].to;
    		
    		if(vis[j]&&Cir[node].empty()){
    			Get(node,j);
    		}
    		if(!vis[j])Find_Circle(j,x,node,tg);
    	}
    	st[top]=0;
    	--top;
    }
    void Dtag(int x,int fa,int tg){
    	Vis[x]=1;Tg[x]=tg;
    	for(int i=H[x];i;i=edge[i].nxt){
    		int j=edge[i].to;
    		if(j==fa)continue;
    		if(Vis[j])continue;
    		Dtag(j,x,tg);
    	}
    }
    void dfs(int x,int fa,int tg){
    	dep[x]=dep[fa]+1;
    	f[x][0]=fa;trg[x]=tg;
    	for(int i=1;i<20;++i)f[x][i]=f[f[x][i-1]][i-1];
    	for(int i=H[x];i;i=edge[i].nxt){
    		int j=edge[i].to;
    		if(j==fa)continue;
    		if(vis[j])continue;
    		dfs(j,x,tg);
    	}
    }
    int LCA(int x,int y){
    	if(dep[x]<dep[y])swap(x,y);
    	for(int i=19;~i;--i)if(f[x][i]&&dep[f[x][i]]>=dep[y])x=f[x][i];
    	if(x==y)return x;
    	for(int i=19;~i;--i)if(f[x][i]&&f[y][i]&&f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    int main(){
    // 	freopen("in.txt","r",stdin);
    // 	freopen("392.out","w",stdout);
    	scanf("%d%d",&n,&q);
    	for(int i=1;i<=n;++i){
    		scanf("%d",&to[i]);
    		link(i,to[i]);
    //		link(to[i],i);?
    		Link(i,to[i]);
    		Link(to[i],i);
    //		printf("(%d %d)
    ",i,to[i]);
    	}
    	for(int i=1;i<=n;++i)
    		if(!vis[i]&&!Vis[i]){
    			top=0;
    			num++;
    			Find_Circle(i,0,num,num);
    			Dtag(i,0,num);
    			if(Cir[num].empty())Cir[num].push_back(i);
    		}
    	for(int i=1;i<=n;++i)vis[i]=0;
    //	puts("Circle:");
    //	for(int i=1;i<=num;++i){
    //		printf("%d:
    ",i);
    //		for(auto v:Cir[i])printf("%d ",v);
    //		puts("");
    //	}
    //	puts("End.");
    	for(int i=1;i<=num;++i)
    		for(auto v:Cir[i])
    			vis[v]=1;
    	for(int i=1;i<=num;++i)
    		for(auto v:Cir[i])
    			dfs(v,0,v);
    	for(int i=1;i<=num;++i){
    		reverse(Cir[i].begin(),Cir[i].end());
    		int Cirsiz=Cir[i].size();
    		for(int j=0;j<Cirsiz;++j){
    			rk[Cir[i][j]]=j+1;
    		}
    	}
    //	for(int i=1;i<=n;++i)printf("%d ",rk[i]);
    //	puts("");
    //	for(int i=1;i<=n;++i)printf("%d ",dep[i]);
    //	puts("");
    //	for(int i=1;i<=n;++i)printf("%d ",vis[i]);
    //	puts("");
    //	for(int i=1;i<=n;++i)printf("%d ",Tg[i]);
    //	puts("");
    //	for(int i=1;i<=n;++i)printf("%d ",trg[i]);
    //	puts("");
    	for(int qq=1;qq<=q;++qq){
    		int a,b;
    		scanf("%d%d",&a,&b);
    		if(Tg[trg[a]]!=Tg[trg[b]]){
    // 			printf("(%d %d)
    ",Tg[a],Tg[b]);
    // 			printf("(%d %d)
    ",trg[a],trg[b]);
    // 			printf("(%d %d)
    ",Tg[trg[a]],Tg[trg[b]]);
    			puts("-1 -1");
    			continue;
    		}
    		if(trg[a]==trg[b]){
    			int L=LCA(a,b);
    			int xx=dep[a]-dep[L];
    			int yy=dep[b]-dep[L];
    			printf("%d %d
    ",dep[a]-dep[L],dep[b]-dep[L]);
    			assert(xx>=0);assert(yy>=0);
    			continue;
    		}
    		int x=dep[a]-1,y=dep[b]-1;
    		assert(x>-1);
    		assert(y>-1);
    		int cirnode=Tg[a];
    		int cirsiz=Cir[cirnode].size();
    		int posx=trg[a];
    		int posy=trg[b];
    		int cirposx=rk[posx];
    		int cirposy=rk[posy];
    		assert(cirposx<=cirsiz);
    		assert(cirposy>=1&&cirposy<=cirsiz);
    		int dy=0,dx=0;
    		if(cirposx<cirposy){
    			dx=cirposy-cirposx;
    			dy=cirsiz-dx;
    		}
    		else{
    			dy=cirposx-cirposy;
    			dx=cirsiz-dy;
    		}
    		assert(dx>=0);
    		assert(dy>=0);
    		int nx=x+dx;
    		int ny=y+dy;
    		assert(nx>=0);
    		assert(ny>=0);
    		//(x,ny)(nx,y)
    		int mx1=Max(x,ny);
    		int mx2=Max(nx,y);
    //		printf("(%d %d)
    (%d %d)
    ",nx,y,x,ny);
    		if(mx1!=mx2){
    			if(mx1>mx2)printf("%d %d
    ",nx,y);
    			else printf("%d %d
    ",x,ny);
    			continue;
    		}
    		else{
    			int mi1=Min(x,ny);
    			int mi2=Min(nx,y);
    			if(mi1!=mi2){
    				if(mi1>mi2)printf("%d %d
    ",nx,y);
    				else printf("%d %d
    ",x,ny);
    				continue;
    			}
    			printf("%d %d
    ",nx,y);
    			continue;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    STM32F407Discovery开发板使用环境搭建
    NIO初识
    Mac下Boost环境搭建
    Android Studio增加NDK代码编译支持--Mac环境
    LNMP平台搭建---PHP安装篇
    LNMP平台搭建---MySQL安装篇
    支付系统流程
    从html字符串中获取div内容---jquery
    记一次进入新公司快速融入开发团队经历
    DataTable复制自身行
  • 原文地址:https://www.cnblogs.com/h-lka/p/15205435.html
Copyright © 2011-2022 走看看