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;
}

浙公网安备 33010602011771号