牛客(多校6):Data structure

在这里插入图片描述
示例1:
输入

10 10 7
4 2
10 4
3 2
6 10
9 2
7 3
1 4
8 2
5 3
8 10 10
2 6 2
3 6 2
4 6 4
3 10 2
8 8 10
3 10 4
2 3 2
2 6 4
1 7 10

输出

0
2
0
1
7
0
2
0
1
0

题意
给定一棵n个节点的有根数,第i个点的编号是i。
有m次询问,每次询问给出l,r,x,求有多少点编号的二元组(i,j)满足 l<=i<j<=r 且 i 和 j 的最近公共祖先是节点x。
分析:

  1. 这个题有矩阵乘法相关规约,难以polylog解决, 考虑根号算法。 以下复杂度分析中n和m都写做n。 这个询问转化一下,
    就是查询x子树在 [l,r] 内的点个数平方, 减去x的每个儿子的子树在 [l,r] 内的点个数平方。 前者平凡,考虑后者:
    这个是一个点所有儿子子树信息的查询,我们可以对原树轻重链剖分,用莫队维护每个点所有轻儿子的答案,这样每次修改沿着重链向上面跳,只会影响O(logn)个位置,然后再求重儿子的答案,总时间复杂度O(n√n
    logn)。

代码:

#include<bits/stdc++.h>
    
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
 
int id[maxn];
struct Qr{
    int l, r, p;
    bool operator < (const Qr &o) const{
        return id[l] == id[o.l] ? (id[l] & 1 ? r > o.r : r < o.r) : id[l] < id[o.l];
    }
};
vector<Qr> qr[maxn];
vector<int> G[maxn];
pii pi[maxn];
int siz[maxn], son[maxn], vis[maxn];
ll ans[maxn], cnt[maxn];
int n, m, rt, tot;
 
const int maxs = 1 << 20;
char buf[maxs], *p1 = buf, *p2 = buf;
inline char fr(){
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, maxs, stdin)) == p1 ? -1 : *p1++;
}
#define gc fr()
inline void read(int &x){
    char ch; while(!isdigit(ch = gc)); x = ch ^ 48;
    while(isdigit(ch = gc)) x = x * 10 + (ch ^ 48);
}
 
#define lowb(x) ((x)&(-(x)))
struct BIT{
    int c[maxn];
    void add(int x, int v){
        while(x <= n) c[x] += v, x += lowb(x);
    }
    int sum(int x){
        int ret = 0;
        while(x > 0) ret += c[x], x -= lowb(x);
        return ret;
    }
    int sum(int l, int r){
        return sum(r) - sum(l - 1);
    }
} bit;
 
void dfs1(int u, int f){
 
    siz[u] = 1, son[u] = 0;
    for(auto &v : G[u]){
 
        if(v == f) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}
 
void cal(int u, int f, int flg){
 
    bit.add(u, flg);
    for(auto &v : G[u]){
 
        if(v == f || vis[v]) continue;
        cal(v, u, flg);
    }
}
 
void add(int u, int f, int bel){
 
    pi[++tot] = {u, bel};
    for(auto &v : G[u]){
 
        if(v == f) continue;
        add(v, u, bel);
    }
}
 
inline ll pw(ll x){
 
    return x * x;
}
 
void dfs2(int u, int f, int flg){
 
    for(auto &v : G[u]){
 
        if(v == f || v == son[u]) continue;
        dfs2(v, u, -1);
    }
    if(son[u]) dfs2(son[u], u, 1), vis[son[u]] = 1;
    for(auto &q : qr[u]){
 
        ans[q.p] -= pw(bit.sum(q.l, q.r));
    }
    cal(u, f, 1);
    for(auto &q : qr[u]){
 
        ans[q.p] += pw(bit.sum(q.l, q.r));
    }
   tot = 0;
    for(auto &v : G[u]){
 
        if(v == f || v == son[u]) continue;
        add(v, u, v);
    }
    sort(pi + 1, pi + 1 + tot);
    for(auto &q : qr[u]){
 
        q.l = lower_bound(pi + 1, pi + 1 + tot, make_pair(q.l, 0)) - pi;
        q.r = upper_bound(pi + 1, pi + 1 + tot, make_pair(q.r, inf)) - pi - 1;
        if(q.l > q.r) q.l = 1, q.r = 0;
    }
    int B = sqrt(tot);
    for(int i = 1; i <= tot; ++i){
 
        id[i] = (i - 1) / B + 1;
    }
    sort(qr[u].begin(), qr[u].end());
    int l = 1, r = 0; ll cur = 0;
    auto add = [&](int x){
        cur += cnt[pi[x].second]++ << 1 | 1;
    };
    auto del = [&](int x){
        cur -= --cnt[pi[x].second] << 1 | 1;
    };
    for(auto &q : qr[u]){
 
        while(l > q.l) add(--l);
        while(r < q.r) add(++r);
        while(l < q.l) del(l++);
        while(r > q.r) del(r--);
        ans[q.p] -= cur;
    }
    for(auto &v : G[u]){
 
        if(v == f || v == son[u]) continue;
        cnt[v] = 0;
    }
    if(son[u]) vis[son[u]] = 0;
    if(flg == -1) cal(u, f, -1);
}
 
int main(){
   
    read(n), read(m), read(rt);
    for(int i = 1; i < n; ++i){
 
        int u, v; read(u), read(v);
        G[u].pb(v), G[v].pb(u);
    }
    for(int i = 1; i <= m; ++i){
 
        int l, r, x; read(l), read(r), read(x);
        qr[x].pb({l, r, i});
    }
    dfs1(rt, 0);
    dfs2(rt, 0, 1);
    for(int i = 1; i <= m; ++i){
 
        ans[i] >>= 1;
        printf("%lld\n", ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr顺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值