前言

如何评价我场上 LCA 没清空 fa 数组没有 AK。

题目翻译

给定 $n$ 个点的一棵树,以及 $k$ 组点 $(u,v)$,表示 $u\to v$ 的简单路径包含特殊边。问至少多少边为特殊边。

多测,$1\leq t\leq 10^4,\sum n\leq10^5,\sum 2^k\leq 2^{20}$。

题目思路

$k$ 很小,考虑状压。$S_i$ 为一个 mask 表示第 $i$ 条边可能出现哪几条特殊边,也就是被哪几个路径包含。

设计 dp 状态 $f_{mask}$ 表示『包含集合 $mask$ 至少需要多少条边』。

直接转移是 $\mathcal O(2^k\times n)$ 的,无法通过。

发现 $k$ 条边至多 $2k$ 个点,直接建虚树大小为 $\mathcal O(k)$。所以我们建立虚树转移 dp。

我们直接对于 $S_i$ 进行去重,得到的集合大小只有 $\mathcal O(k)$ 了。现在复杂度为 $\mathcal O(2^k\times k)$,可以通过。

状压部分的染色,$u\to v$ 的简单路径就是 $u\to \operatorname{lca}(u,v)$ 加上 $v\to \operatorname{lca}(u,v)$,因此暴力跳 $fa$ 数组即可。

完整代码

#include <bits/stdc++.h>
using namespace std;
const int inf = 1e9;
template <typename T>
void chkmn(T &x, T y) { x = min(x, y); }
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++
char buf[1000000], *p1 = buf, *p2 = buf;
template <typename T>
void read(T &x)
{
    x = 0;
    int f = 1;
    char c = getchar();
    for (; c < '0' || c > '9'; c = getchar())
        if (c == '-')
            f = -f;
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    x *= f;
}
template <typename T, typename... Args>
void read(T &x, Args &...y)
{
    read(x);
    read(y...);
}
typedef pair<int, int> pii;
pii E[100020];
int n, m;
vector<int> a[100020];
int dep[100020];
int fa[100020][20];
int S[100020]; // mask
int f[1 << 20];
map<pii, int> mp;
void dfs(int u, int p)
{
    dep[u] = dep[p] + 1;
    fa[u][0] = p;
    for (int i = 1; 1 << i <= dep[u]; i++)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int v : a[u])
        if (v ^ p)
            dfs(v, u);
}
int LCA(int x, int y)
{
    if (dep[x] < dep[y])
        swap(x, y);
    for (int i = 19; i >= 0; i--)
        if (dep[fa[x][i]] >= dep[y])
            x = fa[x][i];
    if (x == y)
        return x;
    for (int i = 19; i >= 0; i--)
        if (fa[x][i] ^ fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}
void solve()
{
    read(n);
    for (int i = 1; i <= n; i++)
    {
        a[i].clear(), S[i] = dep[i] = 0;
        for (int j = 0; j < 20; j++)
            fa[i][j] = 0;
    }

    mp.clear();
    for (int i = 1; i < n; i++)
    {
        int u, v;
        read(u, v);
        E[i] = {u, v};
        a[u].push_back(v);
        a[v].push_back(u);
        mp[{u, v}] = mp[{v, u}] = i;
    }
    dfs(1, 0);
    read(m);
    for (int i = 0; i < m; i++)
    {
        int cnt = 1 << i;
        int u, v;
        read(u, v);
        int lca = LCA(u, v);
        for (int j = u; j != lca; j = fa[j][0])
            S[mp[{j, fa[j][0]}]] |= cnt;
        for (int j = v; j != lca; j = fa[j][0])
            S[mp[{j, fa[j][0]}]] |= cnt;
    }
    for (int i = 0; i < 1 << m; i++)
        f[i] = inf;
    set<int> s;
    for (int i = 1; i < n; i++)
        s.insert(S[i]);
    for (int i : s)
        f[i] = 1;
    // for (int i : s)
    //     cout << i << " ";
    // cout << endl;
    f[0] = 0;
    for (int i = 0; i < 1 << m; i++)
        for (int j : s)
            chkmn(f[i | j], f[i] + 1);
    cout << f[(1 << m) - 1] << '\n';
}
int main()
{
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}
最后修改:2024 年 02 月 24 日
v我50吃疯狂星期四