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