[LNOI2014]LCA
树链剖分好题。
如果我们暴力做的话,那么就是一一求出LCA,暴力统计。
优化的话,我们需要将(dep[LCA(i,z)])转化成我们可以会利用操作。
如下转化:
(dep[u])代表根节点到该结点路径上经过点的数量(包括端点),如果我们计算(dep[LCA(i,z)])那么,我们可以从一个节点向上对于该结点到根节点路径上每个节点进行标记((+1)),另一个节点向上遍历累加路径上的点权即为深度。
这样一来,就将求解深度和树上操作结合。对于多个点也一样。因为它们都有一个公共的(z),故其他的点向上对于路径上的点权(+1)即可,(z)统计它到根节点的路径上点权为多少。
打标记与求和可以用轻重链剖分来做,对于每次都将线段树清空,依次插入节点,时间复杂度为:(O(qnlogn))。
——> 优化:首先有个特点:(iin [l,r])这一段是连续的(显然),上述做法也可以满足不连续的区间(换言之,求答案的时候(i)可以不是连续的)。那么说明对于([l,r]),有其他的性质。我们可以尝试离线(+)差分;
就是说,我们可以把(含(r))(r)之前的所有节点依次上面讨论的那样运行,答案是当前路径点权和减去(l-1)之前的点权和。那么,我们可以对于每一个节点记录作用在它身上的操作是什么;接着我们一个个插入点,离线操作,更新。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#define Re register
#define wht 1,1,n
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define FOR(i, x, y) for(Re int i=x;i<=y;++i)
#define CLR(x, y) memset(x,y,sizeof(x))
using namespace std;
const int SIZE = 5e4 + 5, mod = 201314;
typedef long long LL;
struct node
{
int id, flag, z;
};
struct Segment
{
LL sum, flag;
} T[SIZE << 2];
Segment merge(Segment x, Segment y)
{
Segment res;
res.flag = 0, res.sum = (x.sum + y.sum) % mod;
return res;
}
vector <int> G[SIZE];
vector <node> res[SIZE];
int n, q;
LL ans[SIZE];
int idx = 0, dfn[SIZE], rnk[SIZE], top[SIZE], dep[SIZE], siz[SIZE], fa[SIZE], son[SIZE];
void spread(int p, int l, int r)
{
if(T[p].flag && l < r)
{
int mid = l + ((r - l) >> 1);
T[p << 1].flag = (T[p << 1].flag + T[p].flag) % mod;
T[p << 1 | 1].flag = (T[p << 1 | 1].flag + T[p].flag) % mod;
T[p << 1].sum = (T[p << 1].sum + 1ll * (mid - l + 1) * T[p].flag % mod) % mod;
T[p << 1 | 1].sum = (T[p << 1 | 1].sum + 1ll * (r - mid) * T[p].flag % mod) % mod;
T[p].flag = 0;
}
return;
}
void dfs1(int u, int Fa)
{
fa[u] = Fa, dep[u] = dep[Fa] + 1, siz[u] = 1;
son[u] = -1;
int v;
for(int i = 0; i < G[u].size(); ++ i)
{
v = G[u][i];
if(v == Fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if(son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
}
return;
}
void dfs2(int u, int Top)
{
dfn[u] = ++ idx, top[u] = Top;
rnk[idx] = u;
int v;
if(son[u] != -1) dfs2(son[u], Top);
for(int i = 0; i < G[u].size(); ++ i)
{
v = G[u][i];
if(v != fa[u] && v != son[u]) dfs2(v, v);
}
return;
}
void build(int p, int l, int r)
{
if(l == r)
{
T[p].flag = T[p].sum = 0;
return;
}
int mid = l + ((r - l) >> 1);
build(lson), build(rson);
}
void modify(int p, int l, int r, int L, int R, int val)
{
if(l >= L && r <= R)
{
T[p].sum = (T[p].sum + 1ll * (r - l + 1) * val % mod) % mod;
T[p].flag = (T[p].flag + val) % mod;
return;
}
spread(p, l, r);
int mid = l + ((r - l) >> 1);
if(L <= mid) modify(lson, L, R, val);
if(R > mid) modify(rson, L, R, val);
T[p] = merge(T[p << 1], T[p << 1 | 1]);
return;
}
LL query(int p, int l, int r, int L, int R)
{
if(l >= L && r <= R) return T[p].sum;
spread(p, l, r);
int mid = l + ((r - l) >> 1);
LL res = 0;
if(L <= mid) res = query(lson, L, R);
if(R > mid) res = (res + query(rson, L, R)) % mod;
return res;
}
void modify(int u)
{
while(top[u] != 1)
{
modify(wht, dfn[top[u]], dfn[u], 1);
u = fa[top[u]];
}
modify(wht, dfn[1], dfn[u], 1);
return;
}
LL query(int u)
{
LL res = 0;
while(top[u] != 1)
{
res = (res + query(wht, dfn[top[u]], dfn[u])) % mod;
u = fa[top[u]];
}
res = (res + query(wht, dfn[1], dfn[u])) % mod;
return res;
}
int main()
{
scanf("%d %d", &n, &q);
int v, l, r, z;
FOR(i, 2, n)
{
scanf("%d", &v);//由于节点编号并非从1开始的,当心!
G[i].push_back(v + 1);
G[v + 1].push_back(i);
}
dfs1(1, 0), dfs2(1, 1);
build(wht);
FOR(i, 1, n) res[i].clear();
FOR(i, 1, q)
{
scanf("%d %d %d", &l, &r, &z);
++ r, ++ z;
res[l].push_back((node){i, -1, z});
res[r].push_back((node){i, 1, z});
}
CLR(ans, 0);
FOR(i, 1, n)
{
modify(i);
for(int j = 0; j < res[i].size(); ++ j)
{
node now = res[i][j];
ans[now.id] = (ans[now.id] + now.flag * query(now.z) % mod) % mod;
}
}
FOR(i, 1, q) printf("%lld
", (ans[i] % mod + mod) % mod);
return 0;
}