• 【题解】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;
    }
    
  • 相关阅读:
    Java 基础
    Java 数据类型
    Spring 拦截器实现事物
    SSH 配置日记
    Hibernate 知识提高
    Jsp、Servlet
    leetcode 97. Interleaving String
    leetcode 750. Number Of Corner Rectangles
    leetcode 748. Shortest Completing Word
    leetcode 746. Min Cost Climbing Stairs
  • 原文地址:https://www.cnblogs.com/h-lka/p/15205435.html
Copyright © 2020-2023  润新知