ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【图论/基环树】AcWing 392. 会合

2022-07-01 01:02:58  阅读:153  来源: 互联网

标签:rt ch 基环树 int vis 392 find AcWing


分析

这题就是一道需要分类讨论的图论。。

注意到题目中每个点只有一条出边,也就是说给出的图是一个内向的基环树森林

首先进行预处理:

  • 开一个并查集,这能够将两个点不在同一棵基环树的情况筛掉。
  • 利用内向树随便找一个点跳到基环树的环(环上所有点记为“根”)。然后建反图,在反图上跑一遍 \(\texttt{dfs}\) 以标记 \(vis\),以达到所有点都会在 \(\texttt{dfs}\) 过程中访问且仅访问一次。
  • \(\texttt{dfs}\) 过程中处理 LCA(倍增实现)的祖先数组 p[][]
  • 处理基环树的环的大小,方便求环上 \(x\to y\) 的距离 cycdis

下面开始处理查询:

  • 最简单的情况当然是退化成树上的 LCA 问题,直接检查是否跳到同一个点即可。
  • 如果发现两个点就算跳到了基环树的根也不同,那么就求一次 \(x\) 跳到 \(y\) 的总距离以及 \(y\) 到 \(x\) 的总距离,按照题意所要求的关系进行讨论即可。

事实上,这题的难点在于如何处理需要的信息,主体的查询并不复杂。

更多细节见代码。

彩蛋

《8000ms 与 TLE》

hh.png

hhh.png

// Problem: 会合
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/394/
// Memory Limit: 128 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define endl '\n'
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()

#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=5e5+50;

int n, Q;
int w[N];
vector<int> g[N];

int f[N];

int find(int x){
	return x==f[x]? x: f[x]=find(f[x]);
}

bool vis[N], isrt[N];
int p[N][20], dep[N];

void dfs(int u, int fa, int d, int fir, int sec){
	vis[u]=true;
	dep[u]=d;
	if(!isrt[u]){
		p[u][0]=fa;
		rep(i,1,19) p[u][i]=p[p[u][i-1]][i-1];
	}
	else p[u][0]=u;
	for(auto go: g[u]){
		if(u==sec && go==fir) continue;
		dfs(go, u, (isrt[go]? 0: d+1), fir, sec);
	}
}

pii lca(int u, int v){
	bool swp=false;
	if(dep[u]<dep[v]) swap(u, v), swp=true;
	dwn(i,19,0) if(dep[u]-(1<<i)>=dep[v]) u=p[u][i];
	if(u==v) return {u, v};
	
	dwn(i,19,0){
		int pu=p[u][i], pv=p[v][i];
		if(pu!=pv && !isrt[pu]){
			u=pu, v=pv;
		}
	}
	return swp==false? (pii){p[u][0], p[v][0]}: (pii){p[v][0], p[u][0]};
}

int cycn[N];
int cid[N], ctot, sz[N];

int cycdis(int sz, int u, int v){
	int x=cycn[u], y=cycn[v];
	if(y>x) return y-x;
	return sz-(x-y);
}

int main(){
	cin>>n>>Q;
	rep(i,1,n) f[i]=i;
	rep(i,1,n){
		read(w[i]);
		g[w[i]].pb(i);
		f[find(i)]=find(w[i]);
	}
	
	rep(i,1,n) if(!vis[i]){
		int u=i;
		while(!vis[u]) vis[u]=true, u=w[u];
		int rt=u;
		int ts=0;
		++ctot;
		do{
			isrt[rt]=true;
			cycn[rt]=++ts;
			cid[rt]=ctot;
			sz[ctot]++;
			rt=w[rt];
		}while(rt!=u);
		dfs(u, 0, 0, u, w[u]);
	}
	
	while(Q--){
		int u, v; read(u), read(v);
		if(find(u)!=find(v)){
			puts("-1 -1");
			continue;
		}
		
		auto [x, y]=lca(u, v);
		if(x==y){
			cout<<dep[u]-dep[x]<<' '<<dep[v]-dep[x]<<endl;
			continue;
		}

		int dx=dep[u]-dep[x], dy=dep[v]-dep[y];
		int fir=cycdis(sz[cid[x]], x, y);
		int sec=cycdis(sz[cid[x]], y, x);
		if(max(dx+fir, dy)!=max(dx, dy+sec)){
			if(max(dx+fir, dy)<max(dx, dy+sec)) cout<<dx+fir<<' '<<dy<<endl;
			else cout<<dx<<' '<<dy+sec<<endl;
		}
		else if(min(dx+fir, dy)!=min(dx, dy+sec)){
			if(min(dx+fir, dy)<min(dx, dy+sec)) cout<<dx+fir<<' '<<dy<<endl;
			else cout<<dx<<' '<<dy+sec<<endl;
		}
		else cout<<dx+fir<<' '<<dy<<endl;
	}
	return 0;
}

标签:rt,ch,基环树,int,vis,392,find,AcWing
来源: https://www.cnblogs.com/Tenshi/p/16433149.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有