CF1610H Squid Game 题解
考虑链怎么做。问题变为给一些区间 $[l_i,r_i]$,选最少的点使得每个 $(l_i,r_i)$ 内都有至少一个点,
这个是贪心经典题,按右端点排序即可,考虑在树上类似地贪心。
钦定 $1$ 为根。可以发现选 $1$ 可以解决所有曲链,不妨先考虑只有直链的情况。
设 $d_u$ 表示 $u$ 的深度。对直链 $u\to v$(钦定 $d_u<d_v$),设 $w$ 为 $v$ 的 $d_v-d_u-1$ 级祖先,
则其要求 $\text{subtree}(w)-\text{subtree}(v)$ 内至少有一个点。
把所有直链按 $d_w$ 从大到小排序,每次若一条链还没有满足要求,选择它的 $w$ 即可,这个贪心容易用数据结构维护。
考虑有曲链的情况。显然若此时所有曲链已经满足要求,答案不变。
否则,此时已选的所有点都无法上移,只能再选一个 $1$ 满足剩下的曲链。
#include <cstdio>
#include <algorithm>
using namespace std;
struct E
{
int v, t;
} e[1000050];
struct S
{
int u, v;
} a[1000050], z[1000050];
int n, m, k, r, c, o, q, b[1000050], d[1000050], s[1000050], h[1000050], f[1000050][20], C[1000050];
bool O(S a, S b) { return d[a.u] > d[b.u]; }
void M(int x, int k)
{
for (; x <= n; x += x & -x)
++C[x];
}
int Q(int x, int y)
{
int q = 0;
for (--x; y > x; y &= y - 1)
q += C[y];
for (; x > y; x &= x - 1)
q -= C[x];
return q;
}
void A(int u, int v) { e[++c] = {v, h[u]}, h[u] = c; }
void D(int u, int k)
{
s[u] = 1;
b[u] = ++o;
for (int i = h[u], v; i; i = e[i].t)
if ((v = e[i].v) != k)
{
d[v] = d[f[v][0] = u] + 1;
for (int j = 1; f[v][j - 1]; ++j)
f[v][j] = f[f[v][j - 1]][j - 1];
D(v, u);
s[u] += s[v];
}
}
int K(int u, int v)
{
for (int i = 19; i >= 0; --i)
if (d[f[v][i]] > d[u])
v = f[v][i];
return v;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 2, x; i <= n; ++i)
scanf("%d", &x), A(x, i);
D(1, 0);
for (int i = 0, u, v; i < m; ++i)
{
scanf("%d%d", &u, &v);
if (d[u] > d[v])
swap(u, v);
if (b[v] >= b[u] && b[v] < b[u] + s[u])
{
if (d[v] - d[u] <= 1)
{
puts("-1");
return 0;
}
a[++k] = {u, v};
}
else
z[++r] = {u, v};
}
sort(a + 1, a + k + 1, O);
for (int i = 1, o; i <= k; ++i)
{
o = K(a[i].u, a[i].v);
if (!Q(b[o], b[a[i].v] - 1) && !Q(b[a[i].v] + s[a[i].v], b[o] + s[o] - 1))
M(b[o], 1), ++q;
}
for (int i = 1; i <= r; ++i)
{
if (b[z[i].u] > b[z[i].v])
swap(z[i].u, z[i].v);
if (!Q(1, b[z[i].u] - 1) && !Q(b[z[i].u] + s[z[i].u], b[z[i].v] - 1) && !Q(b[z[i].v] + s[z[i].v], n))
{
++q;
break;
}
}
printf("%d", q);
return 0;
}