CF & AT 做题记录

发布时间 2023-10-14 17:23:56作者: yhk1001

选一些 \(\text{div 1}\) 中的好题总结一下 \(\textrm{qwq}\)
从现在开始像 \(\color{red}\text{r} \color{black}\text{ainboy}\) 一样打比赛 \(\textrm{qaq}\)

$$\textrm{Codeforces Round #902 (CF 1876)}$$

\(\textrm{C Autosynthesis}\)

给定一个 \(n\) 个数的序列 \(a\)。你可以进行若干次操作,每次操作可以在一个数 \(a_i\) 上画圈。
你需要满足以下要求:设你的操作次数为 \(k\)\(p_{1\cdots k}\) 为你每次操作时画圈的 下标,你需要满足没有画圈的数字个数 恰好为 \(k\),且这些数字组成的子序列 \(b_{1\cdots k}\)\(p_{1\cdots k}\) 相等。
请你构造一组方案。如果不存在合法方案,输出 -1
\(1\le n\le 2\times 10^5,1\le a_i\le n\)

一开始就在做,比赛唯一场切的题(
方向很清晰,就是细节有点多(?)

如果一个数 \(x\) 被圈了,那么一定存在一个没有被圈的数,满足其值为 \(x\). 这很有启发性!把每个 \(a_i\) 看作 \(i \to a_i\) ,那么这个序列就转化为一个内向基环树森林,只需对每个连通块分别求解即可。
从没有被圈的数入手,记这种数为 \(1\) 类,被圈的数为 \(0\) 类。考虑 \(u \to fa\) ,如果 \(u\)\(1\) ,那么 \(fa\) 必须是 \(0\). 继续,考虑 \(\forall v \to u\) ,如果 \(u\)\(0\) ,那么所有 \(v\) 也必须是 \(1\). 以上都可以由题意推出。
既然是基环树,首先要找出环。从底向上推,叶子必须是 \(1\),那么不在环上的点的状态唯一确定。对于环上点 \(u\) ,要么是 \(0\) ,即有一个非环节点是 \(1\) 并指向它;要么不确定,即所有指向它的非环节点都是 \(0\)\(u\) 的状态取决于环上 \(pre_u\) 的状态,记这种 \(u\)\(2\) 类。
接下来开始分讨。注意到两个环上 \(0\) 可以相邻,对于形如 \(0222 \dots 2220\) 的序列,可以 \(01\) 交替染色,即 \(0101 \dots 010100\),于是只要环上有 \(0\) 就有解。如果都是 \(2\) ,必须是 \(01\) 交替序列,相同数字必不能相邻,于是环长偶数有解,奇数无解。

点击查看代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

//#define DeBug
//#define LOCAL
// #define TestCases

const int N = 2e5;

int n;
int a[N + 5];

vector<int> e[N + 5];

int notuse[N + 5];

int stk[N + 5], top;
int vis[N + 5];
int seq[N + N + 5], len;
int oncir[N + 5];
void findcir(int u)
{
	if (vis[u])
	{
		len = 0;
		while (stk[top] != u)
		{
			seq[++len] = stk[top];
			top--;
		}
		seq[++len] = u;
		reverse(seq + 1, seq + len + 1);
		top--;
		for (int i = 1; i <= len; i++)
		{
			oncir[seq[i]] = 1;
			seq[i + len] = seq[i];
		}
		return ;
	}
	
	stk[++top] = u;
	vis[u] = 1;
	findcir(a[u]);
	return ;
}
void dfs(int u)
{
	vis[u] = 1;
	if (e[u].empty())
	{
		notuse[u] = 1;
		return ;
	}
	
	int zero = 0, one = 0, son = 0;
	for (unsigned int i = 0; i < e[u].size(); i++)
	{
		int v = e[u][i];
		if (oncir[v])
			continue;

		son++;
		dfs(v);
		if (notuse[v] == 0)
			zero++;
		if (notuse[v] == 1)
			one++;
	}

	if (zero == son)
	{
		if (oncir[u])
			notuse[u] = 2;
		else
			notuse[u] = 1;
		return ;
	}
	notuse[u] = 0;
	return ;
}

void calc(int st)
{
	top = 0;
	findcir(st);

	for (int i = 1; i <= len; i++)
		dfs(seq[i]);

	int zero = 0;
	for (int i = 1; i <= len; i++)
	{
		if (notuse[seq[i]] == 0)
		{
			zero = i;
			break;
		}
	}
	if (zero)
	{
		for (int i = zero + 1; i < zero + len; i++)
		{
			if (notuse[seq[i]] == 0)
				continue;
			notuse[seq[i]] = (notuse[seq[i - 1]] ^ 1);
		}
	}
	else
	{
		if (len & 1)
		{
			puts("-1");
			exit(0);
		}
		for (int i = 1; i <= len; i++)
			notuse[seq[i]] = (i & 1);
	}
	return ;
}

void solve()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", a + i);
		e[a[i]].push_back(i);
		notuse[i] = -1;
	}

	for (int i = 1; i <= n; i++)
		if (!vis[i])
			calc(i);

	int cnt = 0;
	for (int i = 1; i <= n; i++)
		cnt += notuse[i];
	printf("%d\n", cnt);
	for (int i = 1; i <= n; i++)
		if (notuse[i])
			printf("%d ", a[i]);
	printf("\n");
	return ;
}

int main()
{
	#ifdef LOCAL
	freopen("data.in", "r", stdin);
	freopen("mycode.out", "w", stdout);
	#endif
	
	int T = 1;
	
	#ifdef TestCases
	scanf("%d", &T);
	#endif
	
	while (T--)
		solve();
	return 0;
}