LNOI2014 LCA
题目链接:戳我
区间求和可以分成前缀和差分,所以我们可以把每次询问的l,r差分成\([1,r]-[1,l-1]\)
对于每一组询问,我们把\([l,r]\)内的每个点到根的链上都+1,然后x和根的链上的累加和就是LCA的深度总和了。
为什么呢?大家可以画画图,就显而易见了因为题目上说了LCA的深度为它到根结点的距离+1,那么从一个结点出发,给它到根结点的链上的节点累加1的时候,显然是从LCA开始才会对询问点产生贡献的。而这个贡献个就是从LCA开始,到根结点(包含),的点数,而这正等于题目中对LCA深度的定义。
然后这个问题就可以转换成树上区间加和,区间求和啦!这不就是树连剖分的模板了吗,显然一个一个求出来是不现实的,所以我们要考虑一次求出来很多个。所以我们先把所有询问都离线下来,然后我们按照pos从小到大排序,然后如果now==a[i].pos的时候,就可以累加给该询问答案了。
具体的请看代码吧 QAQ
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define MAXN 500010
#define mod 201314
using namespace std;
int n,Q,tt,cnt,tot;
int wt[MAXN],bit[MAXN],id[MAXN];
int head[MAXN<<1],fa[MAXN],topf[MAXN],dep[MAXN],top[MAXN],son[MAXN],siz[MAXN];
struct Edge{int nxt,to;}edge[MAXN<<1];
struct Node{int v,sum,l,r,tag;}t[MAXN<<4];
struct Node2{int pos,num,flag;}a[MAXN];
struct Que{int ans1,ans2,x;}q[MAXN];
inline bool cmp(struct Node2 x,struct Node2 y){return x.pos<y.pos;}
inline void add(int from,int to){edge[++tt].nxt=head[from],edge[tt].to=to,head[from]=tt;}
inline void dfs1(int x,int pre)
{
dep[x]=dep[pre]+1;
siz[x]=1;
int maxx=-1;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==pre) continue;
dfs1(v,x);
siz[x]+=siz[v];
if(siz[v]>maxx) maxx=siz[x],son[x]=v;
}
}
inline void dfs2(int x,int topf)
{
top[x]=topf;
id[x]=++cnt;
if(son[x]) dfs2(son[x],topf);
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v==fa[x]||v==son[x]) continue;
dfs2(v,v);
}
}
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void push_up(int x){t[x].sum=(t[ls(x)].sum+t[rs(x)].sum)%mod;}
inline void f(int x,int k)
{
int l=t[x].l,r=t[x].r;
t[x].tag=(t[x].tag+k)%mod;
t[x].sum=(t[x].sum+1ll*(r-l+1)*k%mod)%mod;
}
inline void push_down(int x)
{
if(t[x].tag)
{
f(ls(x),t[x].tag);
f(rs(x),t[x].tag);
t[x].tag=0;
}
}
inline void build(int x,int l,int r)
{
t[x].l=l,t[x].r=r;
if(l==r){t[x].sum=0;return;}
int mid=(l+r)>>1;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
push_up(x);
}
inline void update(int x,int ll,int rr,int k)
{
int l=t[x].l,r=t[x].r;
if(ll<=l&&r<=rr)
{
f(x,k);
return;
}
int mid=(l+r)>>1;
push_down(x);
if(ll<=mid) update(ls(x),ll,rr,k);
if(mid<rr) update(rs(x),ll,rr,k);
push_up(x);
}
inline int query(int x,int ll,int rr)
{
int l=t[x].l,r=t[x].r;
if(ll<=l&&r<=rr) return t[x].sum;
int mid=(l+r)>>1;
push_down(x);
int cur_ans=0;
if(ll<=mid) cur_ans+=query(ls(x),ll,rr);
if(mid<rr) cur_ans+=query(rs(x),ll,rr);
return cur_ans;
}
inline void ADD(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,id[top[x]],id[x],1);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(1,id[x],id[y],1);
}
inline int QUERY(int x,int y)
{
int cur_ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
cur_ans=(cur_ans+query(1,id[top[x]],id[x]))%mod;
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
cur_ans=(cur_ans+query(1,id[x],id[y]))%mod;
return cur_ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&Q);
n--;
for(int i=1;i<=n;i++)
{
scanf("%d",&fa[i]);
add(fa[i],i);
}
for(int i=1;i<=Q;i++)
{
int l,r;
scanf("%d%d%d",&l,&r,&q[i].x);
a[++tot].pos=l-1,a[tot].num=i,a[tot].flag=0;
a[++tot].pos=r,a[tot].num=i,a[tot].flag=1;
}
build(1,1,n+1);
dfs1(0,0);
dfs2(0,0);
// for(int i=0;i<=n;i++) printf("fa[%d]=%d\n",i,fa[i]); puts("");
// for(int i=0;i<=n;i++) printf("dep[%d]=%d\n",i,dep[i]); puts("");
// for(int i=0;i<=n;i++) printf("son[%d]=%d\n",i,son[i]); puts("");
// for(int i=0;i<=n;i++) printf("siz[%d]=%d\n",i,siz[i]); puts("");
// for(int i=0;i<=n;i++) printf("top[%d]=%d\n",i,top[i]); puts("");
// for(int i=0;i<=n;i++) printf("id[%d]=%d\n",i,id[i]);
int now=-1;
sort(&a[1],&a[tot+1],cmp);
for(int i=1;i<=tot;i++)
{
while(a[i].pos>now)
{
now++;
ADD(now,0);
}
int maomao=a[i].num;
if(a[i].flag==0) q[maomao].ans1=QUERY(q[maomao].x,0);
if(a[i].flag==1) q[maomao].ans2=QUERY(q[maomao].x,0);
}
for(int i=1;i<=Q;i++)
printf("%d\n",((q[i].ans2-q[i].ans1)%mod+mod)%mod);
return 0;
}