其他分享
首页 > 其他分享> > 2022杭电第二场多校 Static Query on Tree

2022杭电第二场多校 Static Query on Tree

作者:互联网

Problem - 7150 Static Query on Tree

题意:

给定一棵以1为根的有向树,\(q\) 次询问,每次询问给出三个集合 \(A,B,C\) 问树中满足可以由集合 \(A, B\) 中各自至少一个点走到的,自身也可以走到 \(C\) 集合中至少一个点的点的个数。

有一个比较显然的树链剖分写法,这里说的是虚树写法,其实也类似树剖写法

假设 \(q = 1\),考虑能否在 \(O(n)\) 的时间内解决问题

我们考虑,从根开始往下走,如果走到 \(C\) 集合的点,于是给该点的子树所有结点染1色,然后如果子树中有 \(A\) 集合的就染2色,有 \(B\) 集合的就染3色,最后看染齐三种颜色的点的个数即可。

于是我们考虑,当每次询问的时候应该如何解决。

注意到,每次询问的时候,我们要考虑的主要还是给定的询问点,其他点只需要知道其个数即可。所以我们只需要在原树的基础上,提取出这些关键点,然后对这些关键点建树即可。

于是剩下的问题就是建树

首先需要预处理一些信息,每个点的时间戳 \(dfn[i]\),深度 \(dep[i]\) 还有他们的公共祖先数组 \(f\),这些一次dfs就可以完成

我们在得到询问点之后,将这些询问点按照时间戳从小到大排序,设这个序列为 \(p\) 序列

考虑扩充这个序列,使得这个序列中的结点为最后虚树的所有结点

\(\forall i \in p\) 考虑加入 \(LCA(p[i], p[i - 1])\),因为是按照时间戳排序的,所以要不 \(LCA(p[i], p[i - 1]) = p[i - 1]\) 或者 \(LCA(p[i], p[i - 1]) = newP\)

考虑倒着来,因为 \(LCA(newP,p[i-2]) = LCA(p[i - 1],p[i - 2])\)

所以我们这样循环一遍就找出了所有的虚树结点

建好虚树之后,我们按照之前的想法dfs一遍即可

#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 2e5 + 10;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

vector<int> g[N], ng[N];
int f[N][22], dep[N], dfn[N];
int stk[N], tt;
int st[N], tag[N];
int timeStamp;
int ans;

void dfs(int u) {
    dfn[u] = ++ timeStamp;
    dep[u] = dep[f[u][0]] + 1;
    for (int v : g[u]) dfs(v);
}
int LCA(int a, int b) {
    if (dep[a] < dep[b]) swap(a, b);
    for (int i = 20; ~i; i -- ) {
        if (dep[f[a][i]] >= dep[b]) a = f[a][i];
    }
    if (a == b) return a;
    for (int i = 20; ~i; i -- ) {
        if (f[a][i] != f[b][i]) a = f[a][i], b = f[b][i];
    }
    return f[a][0];
}
int getDist(int a, int b) {
    int p = LCA(a, b);
    return dep[a] + dep[b] - 2 * dep[p];
}
void calc(int u) {
    for (int v : ng[u]) {
        if (st[u] >> 2 & 1) st[v] |= 1 << 2;
        calc(v);
        if (st[v] & 1) st[u] |= 1;
        if (st[v] >> 1 & 1) st[u] |= 1 << 1;
        if (st[v] == 7) {
            ans += getDist(u, v) - 1;
        }
    }
    if (st[u] == 7) ans ++;
}
inline void solve() {
    timeStamp = 0;
    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) {
        g[i].clear();
        dep[i] = dfn[i] = 0;
        for (int j = 0; j <= 20; j ++ ) f[i][j] = 0;
    }
    for (int i = 2; i <= n; i ++ ) {
        int x; cin >> x;
        f[i][0] = x;
        g[x].push_back(i);
    }
    dfs(1);
    for (int j = 1; j <= 20; j ++ ) {
        for (int i = 1; i <= n; i ++ ) f[i][j] = f[f[i][j - 1]][j - 1];
    }
    while (m -- ) {
        int A, B, C; cin >> A >> B >> C;
        vector<int> a(A), b(B), c(C);
        vector<int> now;
        for (int i = 0; i < A; i ++ ) {
            cin >> a[i];
            if (!st[a[i]]) now.push_back(a[i]);
            st[a[i]] |= (1 << 0);
        }
        for (int i = 0; i < B; i ++ ) {
            cin >> b[i];
            if (!st[b[i]]) now.push_back(b[i]);
            st[b[i]] |= (1 << 1);
        }
        for (int i = 0; i < C; i ++ ) {
            cin >> c[i];
            if (!st[c[i]]) now.push_back(c[i]);
            st[c[i]] |= (1 << 2);
        }

        sort(now.begin(), now.end(), [&](int x, int y) {
            return dfn[x] < dfn[y];
        });
        for (int i = (int)now.size() - 1; i; i -- ) {
            now.push_back(LCA(now[i], now[i - 1]));
        }
        sort(now.begin(), now.end(), [&](int x, int y) {
            return dfn[x] < dfn[y];
        });
        now.erase(unique(now.begin(), now.end()), now.end());
        for (int i = 1; i < now.size(); i ++ ) {
            ng[LCA(now[i - 1], now[i])].push_back(now[i]);
        }
        int root = now[0];
        calc(root);
        cout << ans << endl;
        ans = 0;
        st[1] = tag[1] = 0;
        for (auto ite : now) {
            st[ite] = tag[ite] = 0;
            ng[ite].clear();
        }
    }
}
signed main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
    int T; cin >> T;
    while (T -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}

标签:int,询问,Tree,多校,dfs,st,杭电,dep,LCA
来源: https://www.cnblogs.com/JYF-AC/p/16522380.html