• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【并查集+dfs】codeforces 1833 E. Round Dance

题意

输入一个正整数 \(T(1 \leq T \leq 10^4)\),表示接下来输入 \(T\) 组测试用例,对于每一个测试用例:
第一行,输入一个正整数 \(n(2 \leq n \leq 2 * 10^5)\)
第二行,输入 \(n\) 个正整数 \(a_i(1 \leq a_i \leq n)\),表示节点 \(i\) 到节点 \(a_i\) 存在一条有向边,保证无自环

这 \(n\) 个节点共同围成若干个环,特殊情况除只有两个节点的环以外,每个节点左右各有一个相邻节点。且每个环至少会有 \(2\) 个节点。
问这 \(n\) 个节点最少和最多各能组成多少个环?

题解

建立有向边后,必定可以划分为若干张(弱)连通图,连通图若整张图就是个环且节点数大于 \(2\),则无法再插入更多元素,其他情况均可以与节点数大于等于3的环以外的(弱)连通图进行结合。

总结为:大二有环即为环,否则均为一条链

参考代码

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
using namespace std;

constexpr int N = 2e5 + 7;
int T = 1, n;
int a[N], in[N], dsu[N], sz[N];
bool vis[N];

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

void merge(int x, int y) { 
	int fx = find(x), fy = find(y);
	if (fx == fy) return ;
	dsu[fx] = fy; 
	sz[fy] += sz[fx];
}

void solve() {
	int cnt1 = 0, cnt2 = 0;
	cin >> n;
	for (int i = 1; i <= n; ++ i) {
		cin >> a[i];	
		dsu[i] = i;
		sz[i] = 1;
		in[i] = 0;
		vis[i] = false;
	}
	auto dfs = [&](auto &&dfs, int x) -> void {
		vis[x] = true;
		in[a[x]] ++;
		merge(x, a[x]);
		if (!vis[a[x]]) dfs(dfs, a[x]);
	};
	unordered_map<int, bool> ump;
	for (int i = 1; i <= n; ++ i) if (!vis[i]) dfs(dfs, i);
	for (int i = 1; i <= n; ++ i) {
		bool b = in[i] == 2;
		int ro = find(i);
		if (ump.find(ro) == ump.end()) ump[ro] = b;
		else ump[ro] = b || ump[ro];
	}
	for (auto &it: ump) (it.second || sz[it.first] < 3 ? cnt1 : cnt2) ++;
	cout << cnt2 + (cnt1 > 0) << ' ' << cnt1 + cnt2 << '\n';
}

int main() {
	IOS
	cin >> T;
	while (T --) solve();
	return 0;
}

posted on 2024-10-18 21:30  RomanLin  阅读(86)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3