P4211 [LNOI2014]LCA
有一棵根节点为 1 1 1树,有 m m m次询问,每次给定 l , r , z l, r, z l,r,z,输出 ∑ i = l r d e p [ l c a ( i , z ) ] \sum\limits_{i = l} ^{r} dep[lca(i, z)] i=l∑rdep[lca(i,z)]。
乍一看这题好像无从下手,仔细想想 l c a ( i , z ) lca(i, z) lca(i,z)有何性质,不难发现 l c a ( i , z ) lca(i, z) lca(i,z)一定是在 1 − > z 1->z 1−>z的路径上的,
那么我们的答案求解就可以转换为每次对 i − > 1 i->1 i−>1路径上的点加一,求 z − > 1 z->1 z−>1上路径上的点的和即为我们要求的答案。
考虑如何优化这一过程:
离线求解:
将每个询问 ( l , r , z ) (l, r, z) (l,r,z)拆分成 ( 1 , l − 1 , z ) , ( 1 , r , z ) (1, l - 1, z), (1, r, z) (1,l−1,z),(1,r,z),那么答案就为 ( 1 , r , z ) − ( 1 , l − 1 , z ) (1, r, z) - (1, l - 1, z) (1,r,z)−(1,l−1,z)了,
我们将所有拆分后询问按照 r r r排个序,从小到大每次插入点 i i i,如果当前插入的点 i i i等于每个询问的 r r r则统计一次答案,
两点之间点权的信息更新可以考虑树剖,注意答案的加减,最后输出答案即可,整体复杂度 O ( n log n log n ) O(n \log n \log n) O(nlognlogn)。
#include <bits/stdc++.h>
#define mid (l + r >> 1)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
const int N = 1e5 + 10, mod = 201314;
int head[N], to[N], nex[N], cnt = 1;
int fa[N], dep[N], sz[N], son[N], rk[N], id[N], top[N], tot;
int sum[N << 2], lazy[N << 2], ans[N], n, m;
struct Res {
int u, v, id;
bool operator < (const Res &t) {
return u < t.u;
}
}a[N];
void add(int x, int y) {
to[cnt] = y;
nex[cnt] = head[x];
head[x] = cnt++;
}
void dfs1(int rt, int f) {
fa[rt] = f, dep[rt] = dep[f] + 1, sz[rt] = 1;
for (int i = head[rt]; i; i = nex[i]) {
if (to[i] == f) {
continue;
}
dfs1(to[i], rt);
sz[rt] += sz[to[i]];
if (!son[rt] || sz[son[rt]] < sz[to[i]]) {
son[rt] = to[i];
}
}
}
void dfs2(int rt, int tp) {
top[rt] = tp, rk[++tot] = rt, id[rt] = tot;
if (!son[rt]) {
return ;
}
dfs2(son[rt], tp);
for (int i = head[rt]; i; i = nex[i]) {
if (to[i] == fa[rt] || to[i] == son[rt]) {
continue;
}
dfs2(to[i], to[i]);
}
}
void push_up(int rt) {
sum[rt] = (sum[ls] + sum[rs]) % mod;
}
void push_down(int rt, int l, int r) {
if (lazy[rt]) {
lazy[ls] = (lazy[ls] + lazy[rt]) % mod, lazy[rs] = (lazy[rs] + lazy[rt]) % mod;
sum[ls] = (sum[ls] + 1ll * (mid - l + 1) * lazy[rt]) % mod;
sum[rs] = (sum[rs] + 1ll * (r - mid) * lazy[rt]) % mod;
lazy[rt] = 0;
}
}
void update(int rt, int l, int r, int L, int R, int v) {
if (l >= L && r <= R) {
lazy[rt] = (lazy[rt] + 1) % mod;
sum[rt] = (sum[rt] + r - l + 1) % mod;
return ;
}
push_down(rt, l, r);
if (L <= mid) {
update(lson, L, R, v);
}
if (R > mid) {
update(rson, L, R, v);
}
push_up(rt);
}
int query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return sum[rt];
}
push_down(rt, l, r);
int ans = 0;
if (L <= mid) {
ans = query(lson, L, R);
}
if (R > mid) {
ans = (ans + query(rson, L, R)) % mod;
}
return ans;
}
void update(int rt) {
while (rt) {
update(1, 1, n, id[top[rt]], id[rt], 1);
rt = fa[top[rt]];
}
}
int query(int rt) {
int ans = 0;
while (rt) {
ans = (ans + query(1, 1, n, id[top[rt]], id[rt])) % mod;
rt = fa[top[rt]];
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
scanf("%d %d", &n, &m);
for (int i = 2, fa; i <= n; i++) {
scanf("%d", &fa);
add(fa + 1, i);
}
dfs1(1, 0);
dfs2(1, 1);
for (int i = 1, l, r, u; i <= m; i++) {
scanf("%d %d %d", &l, &r, &u);
l++, r++, u++;
a[i * 2 - 1] = {l - 1, u, i * 2 - 1};
a[i * 2] = {r, u, i * 2};
}
sort(a + 1, a + 1 + 2 * m);
int cur = 1;
while (cur <= 2 * m && a[cur].u == 0) {
cur++;
}
for (int i = 1; i <= n; i++) {
update(i);
while (cur <= 2 * m && a[cur].u == i) {
int res = query(a[cur].v);
if (a[cur].id & 1) {
ans[a[cur].id + 1 >> 1] = (ans[a[cur].id + 1 >> 1] + mod - res) % mod;
}
else {
ans[a[cur].id >> 1] = (ans[a[cur].id >> 1] + res) % mod;
}
cur++;
}
}
for (int i = 1; i <= m; i++) {
printf("%d\n", ans[i]);
}
return 0;
}
在线求解:
考虑主席树,第 i i i棵主席树为前 i − 1 i - 1 i−1棵主席树的信息 + 节点编号为 i i i的点到根节点权值都 + 1 +1 +1的信息。
不难想到,最后我们的答案就是在 [ l , r ] [l, r] [l,r]区间的主席树上查询在 1 − > z 1->z 1−>z之间的点的权值和。
主席树上打 l a z y lazy lazy,空间按道理应该是 O ( n log n log n ) O(n \log n \log n) O(nlognlogn)级别的,支持在线求解时间复杂度也是 O ( n log n log n ) O(n \log n \log n) O(nlognlogn)级别的。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10, mod = 201314;
int head[N], to[N], nex[N], cnt = 1;
int fa[N], son[N], sz[N], top[N], dep[N], rk[N], id[N], tot;
int root[N], ls[N * 200], rs[N * 200], sum[N * 200], lazy[N * 200], n, m, num;
void add(int x, int y) {
to[cnt] = y;
nex[cnt] = head[x];
head[x] = cnt++;
}
void dfs1(int rt, int f) {
fa[rt] = f, dep[rt] = dep[f] + 1, sz[rt] = 1;
for (int i = head[rt]; i; i = nex[i]) {
if (to[i] == f) {
continue;
}
dfs1(to[i], rt);
sz[rt] += sz[to[i]];
if (!son[rt] || sz[son[rt]] < sz[to[i]]) {
son[rt] = to[i];
}
}
}
void dfs2(int rt, int tp) {
top[rt] = tp, rk[++tot] = rt, id[rt] = tot;
if (!son[rt]) {
return ;
}
dfs2(son[rt], tp);
for (int i = head[rt]; i; i = nex[i]) {
if (to[i] == fa[rt] || to[i] == son[rt]) {
continue;
}
dfs2(to[i], to[i]);
}
}
void update(int &rt, int pre, int l, int r, int L, int R, int v) {
rt = ++num;
ls[rt] = ls[pre], rs[rt] = rs[pre], sum[rt] = sum[pre], lazy[rt] = lazy[pre];
sum[rt] = (sum[rt] + 1ll * (min(r, R) - max(l, L) + 1) * v) % mod;
if (l >= L && r <= R) {
lazy[rt] = (lazy[rt] + v) % mod;
return ;
}
int mid = l + r >> 1;
if (L <= mid) {
update(ls[rt], ls[pre], l, mid, L, R, v);
}
if (R > mid) {
update(rs[rt], rs[pre], mid + 1, r, L, R, v);
}
}
int query(int rt1, int rt2, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return (sum[rt2] - sum[rt1] + mod) % mod;
}
int mid = l + r >> 1, ans = (1ll * (min(r, R) - max(l, L) + 1) * (lazy[rt2] - lazy[rt1]) % mod + mod) % mod;
if (L <= mid) {
ans = (ans + query(ls[rt1], ls[rt2], l, mid, L, R)) % mod;
}
if (R > mid) {
ans = (ans + query(rs[rt1], rs[rt2], mid + 1, r, L, R)) % mod;
}
return ans;
}
int query(int l, int r, int rt) {
int ans = 0;
while (rt) {
ans = (ans + query(root[l], root[r], 1, n, id[top[rt]], id[rt])) % mod;
rt = fa[top[rt]];
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = 2, fa; i <= n; i++) {
scanf("%d", &fa);
add(fa + 1, i);
}
dfs1(1, 0);
dfs2(1, 1);
for (int i = 1; i <= n; i++) {
int rt = i;
root[i] = root[i - 1];
while (rt) {
update(root[i], root[i], 1, n, id[top[rt]], id[rt], 1);
rt = fa[top[rt]];
}
}
for (int i = 1, l, r, rt; i <= m; i++) {
scanf("%d %d %d", &l, &r, &rt);
l++, r++, rt++;
printf("%d\n", query(l - 1, r, rt));
}
return 0;
}