Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 735 Solved: 250
[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
Sample Output
8
5
HINT
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
Source
数据已加强 by saffah
思路题+树链剖分。
对于一个询问 r ,我们可以从 r 开始一直向 r 的祖先走,直到走到根,因为 r 与其他节点的LCA必然是这些节点中的一个。
当走到某个结点时,如果他的子孙中包含了 i (第一次),那么这个节点就是 i 与 r 的LCA。
基于这个思路,我们来考虑这道题。
将询问离线,从0~n-1依次插入每一个点,插入时把 i 点到根的路径上的点权值全部 +1 ,可以发现询问 ≤i 的点与 r 的LCA深度之和就是r到根路径上的权值之和,用树链剖分即可实现。
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define LL long long
#define M 100005
using namespace std;
int T,id[M],tot=0,now=0,h[M],dep[M],f[M],son[M],size[M],top[M],n;
LL ans[M];
struct Query
{
int v,z,id;
LL ans;
}q1[M],q2[M];
struct edge
{
int y,ne;
}e[M*2];
struct Segtree
{
int l,r;
LL v,s;
}t[M*4];
bool cmp(Query a,Query b)
{
return a.v<b.v;
}
void read(int &tmp)
{
tmp=0;
char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar());
for (;ch>='0'&&ch<='9';ch=getchar())
tmp=tmp*10+ch-'0';
}
void Addedge(int x,int y)
{
e[++tot].y=y;
e[tot].ne=h[x];
h[x]=tot;
}
void dfs1(int x,int fa,int d)
{
dep[x]=d;
f[x]=fa;
son[x]=0;
size[x]=1;
for (int i=h[x];i;i=e[i].ne)
{
int y=e[i].y;
dfs1(y,x,d+1);
size[x]+=size[y];
if (size[son[x]]<size[y])
son[x]=y;
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
id[x]=++now;
if (son[x])
dfs2(son[x],tp);
for (int i=h[x];i;i=e[i].ne)
{
int y=e[i].y;
if (y==son[x]) continue;
dfs2(y,y);
}
}
void Build(int x,int l,int r)
{
t[x].l=l,t[x].r=r;
t[x].s=t[x].v=0;
if (l==r) return;
int m=(l+r)>>1;
Build(x<<1,l,m);
Build((x<<1)+1,m+1,r);
}
void Push_down(int x)
{
t[x<<1].v+=t[x].v,t[(x<<1)+1].v+=t[x].v;
LL k1=t[x<<1].r-t[x<<1].l+1,k2=t[(x<<1)+1].r-t[(x<<1)+1].l+1;
t[x<<1].s+=(k1*t[x].v),t[(x<<1)+1].s+=(k2*t[x].v);
t[x].v=0;
}
void Push_up(int x)
{
t[x].s=t[x<<1].s+t[(x<<1)+1].s;
}
void add(int x,int l,int r)
{
if (t[x].l>=l&&t[x].r<=r)
{
t[x].v++;
t[x].s+=(t[x].r-t[x].l+1);
return;
}
if (t[x].v) Push_down(x);
int m=(t[x].l+t[x].r)>>1;
if (l<=m) add(x<<1,l,r);
if (r>m) add((x<<1)+1,l,r);
Push_up(x);
}
void Add(int x)
{
int tp1=top[x];
while (tp1)
{
add(1,id[tp1],id[x]);
x=f[tp1];
tp1=top[x];
}
add(1,1,id[x]);
}
LL getsum(int x,int l,int r)
{
if (t[x].l>=l&&t[x].r<=r)
return t[x].s;
if (t[x].v) Push_down(x);
int m=(t[x].l+t[x].r)>>1;
LL ans=0;
if (l<=m) ans+=getsum(x<<1,l,r);
if (r>m) ans+=getsum((x<<1)+1,l,r);
return ans;
}
LL Getsum(int x)
{
int tp1=top[x];
LL ans=0;
while (tp1)
{
ans+=getsum(1,id[tp1],id[x]);
x=f[tp1];
tp1=top[x];
}
return ans+getsum(1,1,id[x]);
}
int main()
{
read(n),read(T);
for (int i=1;i<n;i++)
{
int x;
read(x);
Addedge(x+1,i+1);
}
for (int i=1;i<=T;i++)
{
read(q1[i].v),read(q2[i].v),read(q1[i].z);
q1[i].z++;
q2[i].z=q1[i].z;
q2[i].v++;
q1[i].id=q2[i].id=i;
}
sort(q1+1,q1+1+T,cmp);
sort(q2+1,q2+1+T,cmp);
dfs1(1,0,1),dfs2(1,0);
Build(1,1,n);
int l1=0,l2=0;
q1[T+1].v=q2[T+1].v=n+1;
for (int i=1;i<=n;i++)
{
Add(i);
for (;q1[l1].v<=i;l1++)
{
if (!q1[l1].v) q1[l1].ans=0;
else q1[l1].ans=Getsum(q1[l1].z);
}
for (;q2[l2].v<=i;l2++)
q2[l2].ans=Getsum(q2[l2].z);
}
for (int i=1;i<=T;i++)
ans[q1[i].id]-=q1[i].ans,ans[q2[i].id]+=q2[i].ans;
for (int i=1;i<=T;i++)
printf("%d\n",(int)(ans[i]%201314LL));
return 0;
}
一直WA是因为树链剖分中x=f[tp1],tp1=top[x]写错了