「Luogu P3066」[USACO 12DEC]BarnRunning Away From…
作者:互联网
给出以 1 号点为根的一棵有根树,问每个点的子树中与它距离小于等于 l 的点有多少个。 \((1\le N\le 2\times 10^5,1 \le L \le 10^{18})\)
分析
这道题有很多种方法,我用的是主席树。
很多题解在 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;
}
标签:结点,return,记录,dep,Luogu,Away,int,P3066,define 来源: https://www.cnblogs.com/hlw1/p/12260525.html