妙妙线段树+DFS序判断子孙节点,但似乎还可以树链剖分?(CF Div3 909 G)
G. Unusual Entertainment
原题链接:https://codeforces.com/contest/1899/problem/G
题目大意:
给定一棵树,根节点为1,给定一个\(1\) ~ \(n\) 的排列 \(p\) ,\(q\) 次查询,每次给出 \(l\) 和 \(r\) 还有 \(x\),查询序列 \(p\) 的 \(l\) ~ \(r\) 范围内是否存在节点编号 \(y\) 为 \(x\) 的子孙节点,如果有则输出 \(YES\),否则输出 \(NO\)。
思路:
解法一(官方题解做法):
\(x\) 的子孙节点 \(y\) 在 \(dfs\) 时,入栈时间 \(tin[y]\) 出栈时间 \(tout[y]\),满足:\(tin[x] < tin[y]\) \(tout[x] >= tout[y]\),每次查询序列\(p\) 的 \(l\) ~ \(r\) 中是否存在 \(x\) 的子孙节点就转化为查询序列 \(p\) 的 \(l\) ~ \(r\) 中对应的 \(y = p[i], l <= i <= r\),是否有 \(tin[x] < tin[y]\) and \(tout[x] >= tout[y]\)。构建序列\(a\) 且 \(a[i] = tin[p[i]]\),那么原问题就转化为在序列\(a\) 的 \(l\) ~ \(r\) 中对应的 \(tin[x] <= a[i] <= tout[x]\) 个数的查询。然后就可以维护一个线段树,这里的线段树相当于是一个存储过程的归并排序,每次查询相应区间上的个数。线段树的时空间复杂度均为 \(O(nlgn)\) ,每次通过二分查找即可确定个数。
总时间复杂度为:\(O(nlgn) + O(q*lg(n)^2)\) -> 二分的lg与线段树的lg相乘
解法二(树链剖分)
对整棵树进行树剖后,父节点 \(x\) 的子节点的序列一定在其之后的 \(id[x]+1\) 到 \(id[x]+sz[x]-1\) 之间,利用序列 \(p\) 我们可以得到对应节点编号 \(y\) 在其中的位置 \(py\),将其视为 \(nw[id[y]]\) 就可以利用这个信息去建立线段树,处理方式与方法一相同,也是查询个数,但查询的是 \(x\) 的子孙节点中位置信息在 \(l\) ~ \(r\)之间的个数。
实测来看方法二更快!
代码:
方法一:
#include <bits/stdc++.h>
using namespace std;
#define TII tuple<int, int, int>
#define all(x) x.begin(), x.end()
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10, M = 2e5 + 10, K = 20, mod = 998244353;
const int INF = 0x3f3f3f3f;
const ll INF_L = 1e15;
int n, m;
vector<vector<int>> e;
int timestamp;
vector<int> tin, tout;
struct Node{
int l, r;
vector<int> v;
}tr[4 * N];
inline void add(int a, int b){ e[a].push_back(b); }
void dfs(int x, int father){
tin[x] = timestamp ++;
for(auto y : e[x]){
if(y == father) continue;
dfs(y, x);
}
tout[x] = timestamp;
}
void build(int u, int l, int r, vector<int> &a){
tr[u].v.clear();
if(l == r) tr[u] = {l, r, {a[l]}};
else{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid, a), build(u << 1 | 1, mid + 1, r, a);
merge(all(tr[u << 1].v), all(tr[u << 1 | 1].v), back_inserter(tr[u].v));
}
}
int count(int u, int l, int r, int low, int up){
if(tr[u].l >= l && tr[u].r <= r) return lower_bound(all(tr[u].v), up) - lower_bound(all(tr[u].v), low);
else{
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if(l <= mid) res += count(u << 1, l, r, low, up);
if(r > mid) res += count(u << 1 | 1, l, r, low, up);
return res;
}
}
void solve()
{
int q;
cin >> n >> q;
e.assign(n + 1, vector<int>());
for(int i = 1; i < n; i ++){
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
timestamp = 0;
tin.resize(n + 1);
tout.resize(n + 1);
dfs(1, -1);
vector<int> p(n + 1);
for(int i = 1; i <= n; i ++) cin >> p[i];
vector<int> a(n + 1);
for(int i = 1; i <= n; i ++) a[i] = tin[p[i]];
build(1, 1, n, a);
while(q --){
int l, r, x;
cin >> l >> r >> x;
if(count(1, l, r, tin[x], tout[x])) cout << "Yes\n";
else cout << "No\n";
}
}
int main()
{
cin.tie(0)->sync_with_stdio(false);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
方法二
#include <bits/stdc++.h>
using namespace std;
#define TII tuple<int, int, int>
#define all(x) x.begin(), x.end()
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10, M = 2e5 + 10, K = 20, mod = 998244353;
const int INF = 0x3f3f3f3f;
const ll INF_L = 1e15;
int n, m;
vector<vector<int>> e;
int id[N], nw[N], cnt;
int dep[N], sz[N], top[N], fa[N], son[N];
struct Node{
int l, r;
vector<int> v;
}tr[4 * N];
inline void add(int a, int b){ e[a].push_back(b); }
void dfs1(int x, int father, int depth){
dep[x] = depth, fa[x] = father, sz[x] = 1;
son[x] = 0;
for(auto y : e[x]){
if(y == father) continue;
dfs1(y, x, depth + 1);
sz[x] += sz[y];
if(sz[son[x]] < sz[y]) son[x] = y;
}
}
void dfs2(int x, int t, vector<int> &a){
id[x] = ++ cnt, nw[cnt] = a[x], top[x] = t;
if(!son[x]) return;
dfs2(son[x], t, a);
for(auto y : e[x]){
if(y == fa[x] || y == son[x]) continue;
dfs2(y, y, a);
}
}
void build(int u, int l, int r){
tr[u].v.clear();
if(l == r) tr[u] = {l, r, {nw[l]}};
else{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
merge(all(tr[u << 1].v), all(tr[u << 1 | 1].v), back_inserter(tr[u].v));
}
}
int count(int u, int l, int r, int low, int up){
if(l > r) return 0;
if(tr[u].l >= l && tr[u].r <= r) return lower_bound(all(tr[u].v), up) - lower_bound(all(tr[u].v), low);
else{
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if(l <= mid) res += count(u << 1, l, r, low, up);
if(r > mid) res += count(u << 1 | 1, l, r, low, up);
return res;
}
}
void solve()
{
int q;
cin >> n >> q;
e.assign(n + 1, vector<int>());
for(int i = 1; i < n; i ++){
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
dfs1(1, -1, 0);
vector<int> p(n + 1);
for(int i = 1; i <= n; i ++) cin >> p[i];
vector<int> a(n + 1);
for(int i = 1; i <= n; i ++) a[p[i]] = i;
cnt = 0;
dfs2(1, 1, a);
build(1, 1, n);
while(q --){
int l, r, x;
cin >> l >> r >> x;
if(count(1, id[x], id[x] + sz[x] - 1, l, r + 1)) cout << "Yes\n";
else cout << "No\n";
}
}
int main()
{
cin.tie(0)->sync_with_stdio(false);
cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号