背景:
这都是套路题?
见的题太少了(貌似在模拟赛见过???)。
题目传送门:
https://loj.ac/problem/2558
https://loj.ac/problem/3088
题意:
P
2588
P2588
P2588:一棵树,多组询问,给出
x
,
y
,
z
x,y,z
x,y,z,求
∑
i
=
x
y
d
e
p
[
lca
i
,
z
]
\sum_{i=x}^{y}dep[\text{lca}_{i,z}]
∑i=xydep[lcai,z]。
P
3088
P3088
P3088:一棵树,多组询问,给出
x
,
y
x,y
x,y,求
∑
i
=
1
x
d
e
p
[
lca
i
,
y
]
z
\sum_{i=1}^{x}dep[\text{lca}_{i,y}]^z
∑i=1xdep[lcai,y]z。
思路:
其实两个题差不多。
你要解决的问题都是形如
∑
i
l
c
a
(
i
,
x
)
\sum_ilca(i,x)
∑ilca(i,x)的问题。
方法就是从
i
∈
[
x
,
y
]
i∈[x,y]
i∈[x,y]的从
i
i
i到
1
1
1路径全部
+
1
+1
+1,统计答案时就是从
1
1
1(根节点)到
z
z
z的路径的权值和。
为什么是对的呢?
对
d
e
p
[
l
c
a
]
dep[lca]
dep[lca]的定义是
l
c
a
lca
lca的深度为它到根结点的距离
+
1
+1
+1,那么从一个结点出发,给它到根结点的链上的节点累加1的时候,显然是从
l
c
a
lca
lca开始才会对询问点产生贡献的。而这个贡献个就是从
l
c
a
lca
lca开始,到根结点(包含根节点)的点数,而这正等于题目中对
l
c
a
lca
lca深度的定义。
由于 P 2588 P2588 P2588求的是 ∑ x y \sum_{x}^{y} ∑xy的贡献,等同于 ∑ 1 y − ∑ 1 x − 1 \sum_1^y-\sum_1^{x-1} ∑1y−∑1x−1。实现方法就是离线按 x − 1 , y x-1,y x−1,y的值升序,用树剖维护(可以差分,也可以不用,我当然~~~不用了)。
P
3088
P3088
P3088有一个
z
z
z次方,怎么办呢?
考虑
P 2558 P2558 P2558代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 201314
using namespace std;
int n,m,q,u=0,len=0,op=0;
struct node1{int x,y,next;} a[200010];
struct node2{int d,l,r,lc,rc,lazy,n;} tr[200010];
struct node3{int x,y,id,t;} b[200010];
int last[200010],p[200010],tot[200010],son[200010],fa[200010],dep[200010],ys[200010],yss[200010],top[200010],ans[200010];
void ins(int x,int y)
{
a[++len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;
}
void build(int l,int r)
{
int now=++len;
tr[now]=(node2){0,l,r,-1,-1,0,r-l+1};
if(l<r)
{
int mid=(l+r)>>1;
tr[now].lc=len+1; build(l,mid);
tr[now].rc=len+1; build(mid+1,r);
}
}
void update(int now)
{
if(tr[now].lazy)
{
int lc=tr[now].lc,rc=tr[now].rc;
if(lc!=-1) tr[lc].d+=tr[lc].n*tr[now].lazy,tr[lc].lazy+=tr[now].lazy;
if(rc!=-1) tr[rc].d+=tr[rc].n*tr[now].lazy,tr[rc].lazy+=tr[now].lazy;
tr[now].lazy=0;
}
}
void change(int now,int l,int r,int k)
{
update(now);
if(tr[now].l==l&&tr[now].r==r)
{
tr[now].d+=tr[now].n*k;
tr[now].lazy+=k;
return;
}
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(mid+1<=l) change(rc,l,r,k);
else if(r<=mid) change(lc,l,r,k);
else change(lc,l,mid,k),change(rc,mid+1,r,k);
tr[now].d=tr[lc].d+tr[rc].d;
}
int findsum(int now,int l,int r)
{
update(now);
if(tr[now].l==l&&tr[now].r==r) return tr[now].d;
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(mid+1<=l) return findsum(rc,l,r);
else if(r<=mid) return findsum(lc,l,r);
else return findsum(lc,l,mid)+findsum(rc,mid+1,r);
}
void dfs1(int x)
{
tot[x]=1;
son[x]=0;
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(y!=fa[x])
{
fa[y]=x;
dep[y]=dep[x]+1;
dfs1(y);
if(tot[son[x]]<tot[y]) son[x]=y;
tot[x]+=tot[y];
}
}
}
void dfs2(int x,int tp)
{
ys[x]=++u;
top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if(y!=son[x]&&y!=fa[x]) dfs2(y,y);
}
}
void add(int x,int y)
{
int tx=top[x],ty=top[y];
while(tx!=ty)
{
if(dep[tx]>dep[ty]) swap(x,y),swap(tx,ty);
change(1,ys[ty],ys[y],1);
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y);
change(1,ys[x],ys[y],1);
}
int solve(int x,int y)
{
int tx=top[x],ty=top[y],ans=0;
while(tx!=ty)
{
if(dep[tx]>dep[ty]) swap(x,y),swap(tx,ty);
ans+=findsum(1,ys[ty],ys[y]);
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y);
return ans+findsum(1,ys[x],ys[y]);
}
bool cmp(node3 x,node3 y)
{
return x.x<y.x;
}
int main()
{
int x,y,z;
scanf("%d %d",&n,&m);
for(int i=2;i<=n;i++)
{
scanf("%d",&x);
x++;
ins(x,i),ins(i,x);
}
len=0;
build(1,n);
fa[1]=0;
dep[1]=1;
dfs1(1);
dfs2(1,0);
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&x,&y,&z);
x++,y++,z++;
b[++op]=(node3){x-1,z,i,-1};
b[++op]=(node3){y,z,i,1};
}
sort(b+1,b+op+1,cmp);
int now=0;
for(int i=1;i<=op;i++)
{
while(now<b[i].x)
{
now++;
add(1,now);
}
ans[b[i].id]+=b[i].t*solve(1,b[i].y);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]%mod);
}