P4211 [LNOI2014]LCA
一棵 \(n\) 个节点的有根树,\(m\) 次询问,每次给出 \(l,r,z\) 求
\(\sum_{i = l}^rdep[LCA(i,z)]\)
\(n\leq50000,1\leq m\leq 50000\)
solution
超级神奇的转化
该询问可以转化为把 \(l-r\) 区间内的所有点各自到根节点的所有权值都 +1,最后求出 \(z\) 到根节点的权值和就是答案
树上区间加肯定是树剖解决,如果每次询问都循环每个点进行区间加,然后清空,时间复杂度 \(O(mnlog^2n)\) 那么就和暴力没什么区别了
考虑怎么优化掉 m 这个复杂度
考虑离线,差分
把所有的询问都按照左端点从小到大排序,然后对每个点扫一遍,同时进行树上区间加,每个区间的答案差分得到就好了
code
/*
work by: Ariel
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
#define int long long
using namespace std;
const int mod = 201314;
const int MAXN = 5e5 + 5;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int n, m;
struct edge{int v, nxt;}e[MAXN << 1];
int E, head[MAXN];
void add_edge(int u, int v) {
e[++E] = (edge) {v, head[u]};
head[u] = E;
}
struct Question{
int id, pos, z, flg;
bool operator < (const Question &x) const{
return pos < x.pos;
}
}q[MAXN];
namespace Seg{
#define lson rt << 1
#define rson rt << 1|1
struct Tree{int len, sum, lzy;}tree[MAXN << 2];
void push_up(int rt) {
tree[rt].sum = (tree[lson].sum + tree[rson].sum) % mod;
}
void build(int rt, int l, int r) {
tree[rt].len = r - l + 1;
if (l == r){
tree[rt].sum = tree[rt].lzy = 0;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
push_up(rt);
}
void push_down(int rt) {
if (!tree[rt].lzy) return;
tree[lson].lzy += tree[rt].lzy, tree[rson].lzy += tree[rt].lzy;
tree[lson].sum = (tree[lson].sum + tree[lson].len * tree[rt].lzy) % mod;
tree[rson].sum = (tree[rson].sum + tree[rson].len * tree[rt].lzy) % mod;
tree[rt].lzy = 0;
}
void update(int rt, int l, int r, int L, int R) {
if (l > R || r < L) return ;
if (L <= l && r <= R) {
tree[rt].lzy += 1ll;
tree[rt].sum += tree[rt].len;
return ;
}
push_down(rt);
int mid = (l + r) >> 1;
if (L <= mid) update(lson, l, mid, L, R);
if (R > mid) update(rson, mid + 1, r, L, R);
push_up(rt);
}
int query(int rt, int l, int r, int L, int R) {
if (l > R || r < L) return 0;
if (L <= l && r <= R) return tree[rt].sum;
push_down(rt);
int mid = (l + r) >> 1, ret = 0;
if (L <= mid) ret += query(lson, l, mid, L, R);
if (R > mid) ret += query(rson, mid + 1, r, L, R);
return ret;
}
}
using namespace Seg;
int dep[MAXN], siz[MAXN], son[MAXN], top[MAXN], fa[MAXN], id[MAXN], o;
namespace Cut{
void dfs(int x, int f) {
dep[x] = dep[f] + 1, fa[x] = f, siz[x] = 1;
for (int i = head[x]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == f) continue;
dfs(v, x);
siz[x] += siz[v];
if (siz[v] > siz[son[x]]) son[x] = v;
}
}
void dfs2(int x, int tp) {
top[x] = tp, id[x] = ++o;
if (son[x]) dfs2(son[x], tp);
for (int i = head[x]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa[x] || v == son[x]) continue;
dfs2(v, v);
}
}
void Modify(int x, int y) {
while(top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
update(1, 1, n, id[top[x]], id[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
update(1, 1, n, id[x], id[y]);
}
int Query(int x, int y) {
int ret = 0;
while(top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ret = (ret + query(1, 1, n, id[top[x]], id[x])) % mod;
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
ret = (ret + query(1, 1, n, id[x], id[y]));
return ret;
}
}
using namespace Cut;
int cnt, Ans[MAXN];
signed main() {
n = read(), m = read();
for (int i = 2, u; i <= n; i++) {
u = read() + 1;
add_edge(i, u), add_edge(u, i);
}
build(1, 1, n);
dfs(1, 0);
dfs2(1, 0);
for (int i = 1, l, r, z; i <= m; i++) {
l = read() + 1, r = read() + 1, z = read() + 1;
q[++cnt] = (Question) {i, l - 1, z, 0};
q[++cnt] = (Question) {i, r, z, 1};
}
sort(q + 1, q + cnt + 1);
int cur = 1;
for (int i = 1; i <= cnt; i++) {
while(cur <= q[i].pos) Modify(1, cur++);
if (q[i].flg == 0) Ans[q[i].id] -= Query(1, q[i].z);
else Ans[q[i].id] += Query(1, q[i].z);
Ans[q[i].id] += mod;
Ans[q[i].id] %= mod;
}
for (int i = 1; i <= m; i++) printf("%lld\n", Ans[i]);
}

浙公网安备 33010602011771号