3626: [LNOI2014]LCA
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1817 Solved: 702
[ Submit][ Status][ Discuss]
Description
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
Input
第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。
Output
输出q行,每行表示一个询问的答案。每个答案对201314取模输出
Sample Input
5 2
0
0
1
1
1 4 3
1 4 2
0
0
1
1
1 4 3
1 4 2
Sample Output
8
5
5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
Source
两个点的LCA等价于。。
如果每个点开个bitset,从根到它的路径上存在的点标1
两个点的bitset进行&,,结果的1的个数就是深度
考虑没有l的限制
一个点到根的路径上所有点权+1
[1,r]范围内,原式的ans等价于从z点走到根,权值求和
那么题目有l的限制?
离线一下就好
赋值操作树链剖分就好
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
const int maxn = 5E4 + 10;
const int T = 80;
typedef long long LL;
const LL mo = 201314;
struct Q{
int z,pos,Multi,flag;
Q(int _z = 0,int _pos = 0,int _Multi = 0,int _flag = 0) {
z = _z; pos = _pos; Multi = _Multi; flag = _flag;
}
bool operator < (const Q &b) const{return flag < b.flag;}
}query[maxn*2];
int n,m,dfs_clock,cnt,fa[maxn],len[maxn],pos[maxn],
siz[maxn],Nex[maxn],po[maxn],Root[maxn],dfn[maxn];
LL c[maxn*T],Add[maxn*T],ans[maxn];
bool Huge[maxn];
vector <int> v[maxn];
void dfs1(int x)
{
siz[x] = 1; int Max = 0,Pos;
for (int i = 0; i < v[x].size(); i++) {
int to = v[x][i];
dfs1(to); siz[x] += siz[to];
if (siz[to] > Max) Max = siz[to],Pos = to;
}
if (siz[x] > 1) Huge[Pos] = 1,Nex[x] = Pos;
}
void dfs2(int x,int Len)
{
dfn[x] = ++dfs_clock; po[dfs_clock] = x;
if (Huge[x]) ++Len;
if (Nex[x]) dfs2(Nex[x],Len);
if (siz[x] == 1 && Huge[x]) {len[x] = Len; pos[x] = 1;}
if (siz[x] > 1 && Huge[x]) {len[x] = len[Nex[x]]; pos[x] = pos[Nex[x]] + 1;}
for (int i = 0; i < v[x].size(); i++) {
int to = v[x][i];
if (Huge[to]) continue;
dfs2(to,0);
}
}
int getint()
{
char ch = getchar();
int ret = 0;
while (ch < '0' || '9' < ch) ch = getchar();
while ('0' <= ch && ch <= '9') ret = ret*10 + ch - '0',ch = getchar();
return ret;
}
void pushdown(int o,int l,int r)
{
if (Add[o]) {
if (l == r) return;
int lc = 2*o,rc = 2*o + 1,mid = (l+r) >> 1;
c[lc] += 1LL*(mid - l + 1)*Add[o]; Add[lc] += Add[o];
c[rc] += 1LL*(r - mid)*Add[o]; Add[rc] += Add[o];
Add[o] = 0;
}
}
void Insert(int o,int l,int r,int ql,int qr)
{
if (ql <= l && r <= qr) {
c[o] += 1LL*(r - l + 1);
++Add[o]; return;
}
int mid = (l + r) >> 1;
pushdown(o,l,r);
if (ql <= mid) Insert(2*o,l,mid,ql,qr);
if (qr > mid) Insert(2*o+1,mid+1,r,ql,qr);
c[o] = c[2*o] + c[2*o+1];
}
LL Query(int o,int l,int r,int ql,int qr)
{
if (l > r) return 0;
if (ql <= l && r <= qr) return c[o];
pushdown(o,l,r);
int mid = (l + r) >> 1;
LL ret = 0;
if (ql <= mid) ret += Query(2*o,l,mid,ql,qr);
if (qr > mid) ret += Query(2*o+1,mid+1,r,ql,qr);
return ret;
}
void Solve(int now)
{
LL sum = 0;
for (int z = query[now].z; z; z = fa[z])
if (!Huge[z]) sum += Query(1,1,n,dfn[z],dfn[z]);
else {
int L = dfn[z] - len[z] + pos[z];
sum += Query(1,1,n,L,dfn[z]);
z = po[L];
}
ans[query[now].pos] += 1LL*query[now].Multi*sum;
}
int main()
{
#ifdef DMC
freopen("lca1.in","r",stdin);
freopen("test.txt","w",stdout);
#endif
n = getint(); m = getint();
for (int i = 2; i <= n; i++) {
int x = getint(); fa[i] = ++x;
v[x].push_back(i);
}
dfs1(1); dfs2(1,0);
int tot = 0;
for (int i = 1; i <= m; i++) {
int l = getint(),r = getint() + 1,z = getint() + 1;
if (l) query[++tot] = Q(z,i,-1,l);
query[++tot] = Q(z,i,1,r);
}
sort(query + 1,query + tot + 1);
int tail = 1;
for (int i = 1; i <= n; i++) {
for (int j = i; j; j = fa[j])
if (!Huge[j]) Insert(1,1,n,dfn[j],dfn[j]);
else {
int L = dfn[j] - len[j] + pos[j];
Insert(1,1,n,L,dfn[j]);
j = po[L];
}
while (tail <= tot && query[tail].flag <= i) Solve(tail++);
}
for (int i = 1; i <= m; i++) printf("%lld\n",ans[i] % mo);
return 0;
}
此题用可持久化线段树可以做到在线询问,但是,,苟蒻不会写。GG