AT_abc430_f [ABC430F] Back and Forth Filling 题解

题目传送门

思路

\(i \to j\) 有边代表 \(i\)\(j\) 的左边。

题目中的 \(n - 1\) 个条件就可以变成一个 \(n\) 个点的 DAG。那我们会发现,对于点 \(u\),他的所有祖先节点都需要填在 \(u\) 的左边,他的所有子孙节点都需要填在 \(u\) 的右边。

那我们分别对正图和反图跑拓扑排序就可以得出每一个 \(u\) 的祖先个数 \(cntp_u\) 和子孙个数 \(cnts_u\)。那每一个 \(u\) 都可以填在 \([cntp_u + 1, n - cnts_u]\) 这个区间中。用差分求解即可。注意多测清空。

时间复杂度 \(\mathcal{O}(\sum n)\),可以通过此题。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 5;

int t, n;
int cntp[N], cnts[N], deg[N], d[N];
char s[N];
vector<int> tr[N];

void solve()
{
	scanf("%d%s", &n, s + 1);
	for (int i = 1; i < n; i++)
	{
		if (s[i] == 'L') tr[i + 1].push_back(i), deg[i]++;
		else tr[i].push_back(i + 1), deg[i + 1]++;
	}
	queue<int> q;
	for (int i = 1; i <= n; i++)
		if (!deg[i]) q.push(i);
	while (q.size())
	{
		int u = q.front();
		q.pop();
		for (int v : tr[u])
		{
			cntp[v] += cntp[u] + 1;
			q.push(v);
		}
	}
	for (int i = 1; i <= n; i++)
		deg[i] = 0, tr[i].clear();
	for (int i = 1; i < n; i++)
	{
		if (s[i] == 'L') tr[i].push_back(i + 1), deg[i + 1]++;
		else tr[i + 1].push_back(i), deg[i]++;
	}
	for (int i = 1; i <= n; i++)
		if (!deg[i]) q.push(i);
	while (q.size())
	{
		int u = q.front();
		q.pop();
		for (int v : tr[u])
		{
			cnts[v] += cnts[u] + 1;
			q.push(v);
		}
	}
	for (int i = 1; i <= n; i++)
	{
		int l = cntp[i] + 1, r = n - cnts[i];
		if (l <= r)
		{
			d[l]++;
			if (r + 1 <= n) d[r + 1]--;
		}
	}
	for (int i = 1; i <= n; i++)
		d[i] += d[i - 1];
	for (int i = 1; i <= n; i++)
		printf("%d ", d[i]);
	printf("\n");
	for (int i = 0; i <= n + 1; i++)
		deg[i] = d[i] = cntp[i] = cnts[i] = 0, tr[i].clear();
}

int main()
{
	scanf("%d", &t);
	while (t--) solve();
	return 0;
}
posted @ 2025-12-13 21:07  lucasincyber  阅读(6)  评论(0)    收藏  举报