「Luogu P3066」[USACO 12DEC]BarnRunning Away From…

给出以 1 号点为根的一棵有根树,问每个点的子树中与它距离小于等于 l 的点有多少个。 \((1\le N\le 2\times 10^5,1 \le L \le 10^{18})\)

Luogu

分析

这道题有很多种方法,我用的是主席树。

很多题解在 dfs 时记录的都是 dfs 序,我记录的是和树上莫队一样的欧拉序,在 dfs 到当前结点 i 时记录 一个时间戳,并记录 st[i] = 当前的时间戳,当访问完它的子树时再记录一次时间戳,并记录 ed[i] = 当前时间戳,在建树的时候我们就按照记录的欧拉序来建。我们会发现,一个结点在欧拉序中会出现两次,而这两次中间的结点就是它的子树,要求当前结点的子树的信息时:query(tr[st[i] - 1], tr[ed[i]], l, r, k) ,但注意,在欧拉序中,每个结点出现两次,所以我们计算也计算了两次,最后要除以 2 。

我们在 dfs 的过程中,用 dep[i] 记录 i 到根结点的距离,然后可以将在 u 的子树中,与 u 相距小于等于 l 转化为 dep[v] 与 dep[u] 的差小于等于 l ,于是我们询问:query(tr[st[u] - 1], tr[ed[u]], l, r, dep[u] + l) ,接着就是裸的主席树了。

最后要注意这题要开 long long ,然后需要离散化,我们也要将所有的 dep[i] + l 加入离散化数组中。

代码

这份代码卡卡常也就过了 :D

#include <bits/stdc++.h>

#define N 400005
#define re register
#define ll long long
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
#define REP(i, l, r) for (int i = (l); i != (r); ++i)
#define FOR(i, l, r) for (int i = (l); i <= (r); ++i)
#define DRP(i, l, r) for (int i = (l); i != (r); --i)
#define DFR(i, l, r) for (int i = (l); i >= (r); --i)

using namespace std;

int gi() {
    int x = 0, f = 1; char c = getchar();
    for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

int n, tot, num;
ll m, val[N], hs[N << 2], dep[N], len;
int to[N], nxt[N], hd[N], ecnt;
int ord[N], st[N], ed[N];
int tr[N], cnt[N << 5], L[N << 5], R[N << 5];

inline int get(ll k) { return lower_bound(hs + 1, hs + 1 + len, k) - hs; }

inline void insert(int u, int v, ll w) {
    to[++ecnt] = v, val[ecnt] = w, nxt[ecnt] = hd[u], hd[u] = ecnt;
}

inline void dfs(int u, int fa) {
    ord[++num] = u, st[u] = num;
    for (re int i = hd[u]; i; i = nxt[i]) {
        int v = to[i]; ll w = val[i];
        if (v == fa) continue;
        hs[++len] = dep[v] = dep[u] + w;
        hs[++len] = dep[v] + m;
        dfs(v, u);
    }
    ord[++num] = u, ed[u] = num;
}

inline void modify(int lst, int &now, ll l, ll r, ll k) {
    if (!now) now = ++tot;
    cnt[now] = cnt[lst] + 1;
    if (l == r) return;
    re ll mid = l + r >> 1;
    if (k <= mid) R[now] = R[lst], modify(L[lst], L[now], l, mid, k);
    else L[now] = L[lst], modify(R[lst], R[now], mid + 1, r, k);
}

inline ll query(int u, int v, ll l, ll r, ll k) {
    if (k > r || k < l) return 0;
    if (l == r && l == k) return cnt[v] - cnt[u];
    else if (l == r) return 0;
    re ll mid = l + r >> 1, sum = cnt[L[v]] - cnt[L[u]];
    if (k <= mid) return query(L[u], L[v], l, mid, k);
    else return query(R[u], R[v], mid + 1, r, k) + sum;
}

int main() {
    re int u, k; re ll l;
    len = n = gi(); scanf("%lld", &m);
    hs[1] = m;
    for (re int i = 2; i <= n; ++i) {
        u = gi(); scanf("%lld", &l);
        insert(u, i, l), insert(i, u, l);
        hs[i] = l;
    }
    dfs(1, 0);
    sort(hs + 1, hs + 1 + len);
    len = unique(hs + 1, hs + 1 + len) - hs - 1;
    for (re int i = 1; i <= num; ++i) modify(tr[i - 1], tr[i], 1, len, get(dep[ord[i]]));
    for (re int i = 1; i <= n; ++i) {
        k = get(m + dep[i]);
        printf("%lld\n", query(tr[st[i] - 1], tr[ed[i]], 1, len, k) >> 1);
    }
    return 0;
}
上一篇:将加入小米,语音识别大牛、Kaldi之父Daniel Povey宣布年底前来中国工作


下一篇:lca lowest common ancestor 最小公共祖先 How far away?(安达里士no.3)