妙妙线段树+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;
}
posted @ 2025-05-17 15:20  宋佳奇  阅读(25)  评论(1)    收藏  举报