题解
首先考虑到两点:
1.若确定
1
≤
j
≤
i
,
l
c
a
(
s
j
,
s
i
)
=
s
i
1 \leq j \leq i,lca(s_j,s_i)=s_i
1≤j≤i,lca(sj,si)=si,那么对于
k
≥
i
k \geq i
k≥i,我们只需要考虑
s
i
s_i
si 对
k
k
k 的影响即可。也就是该性质具有无前效性。
2.对于满足条件的点
{
a
i
}
\{a_i\}
{ai},一定满足
a
i
a_i
ai 为
s
1
s_1
s1 的祖先,而且是往根节点递增的祖先。也就是该性质具有前缀性。
所以,可以由这两点性质入手,由于这道题卡空间,所以我们想到一道很像的题——楼房重建。
具体地,我们把区间拆成 l o g log log 个,然后预处理线段树上的区间 l , r l,r l,r,维护以 l l l 为起点, l ≤ j ≤ i , l c a ( s j , s i ) = s i l \leq j \leq i,lca(s_j,s_i)=s_i l≤j≤i,lca(sj,si)=si 的点数,以及到 r r r 之前最大(深度最小) 的 l c a lca lca 为哪个(因为 l c a lca lca 是往根递增的)。
然后去写写会发现求出答案我们只需要考虑一点:前缀 l c a lca lca 为 k k k 的情况下,区间(线段树上的) l , r l,r l,r 中为前缀 l c a lca lca 的点数数量。形式化地,求出对于区间 l , r l,r l,r 中的一点 i i i 满足 l ≤ j ≤ i , l c a ( s j , s i , k ) = s i l \leq j \leq i,lca(s_j,s_i,k)=s_i l≤j≤i,lca(sj,si,k)=si 的点数数量。
我们考虑其右区间 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的 l c a lca lca 最大值,若其不为 k k k 的祖先,那么显然该区间 [ l , r ] [l,r] [l,r] 内没有点能满足条件。
考虑为
k
k
k 祖先的情况。我们再考虑左区间
[
l
,
m
i
d
]
[l,mid]
[l,mid],若其
l
c
a
lca
lca 最大值不为
k
k
k 的祖先,那么能找到为
k
k
k 祖先的点显然在右区间内,递归处理右区间即可。
否则,我们递归处理左区间,求出左区间内的点数。然后我们考虑答案形式:一定是从左区间某一个点开始,
l
c
a
lca
lca 递增的序列。区间
[
l
,
r
]
[l,r]
[l,r] 不考虑
k
k
k 的影响下,右区间
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r] 的答案只与左区间的最大值有关,在这里也只与左区间的最大值有关,与
k
k
k 无关。所以我们可以用 区间
[
l
,
r
]
[l,r]
[l,r] 的最大值减去左区间的最大值得到右区间的点数,再加上递归处理左区间得到的答案,就是区间
[
l
,
r
]
[l,r]
[l,r] 在
k
k
k 的影响下的点数。
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
int k=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')k=k*10+c-'0',c=getchar();
return k*f;
}
const int N=700005;
int n,m,q,a[N],faa;
int tot,ver[N<<1],fst[N],nxt[N<<1];
int lt[N],rt[N],dfn,zx,d[N],lc[N<<2];
int fa[N],sum[N<<2],sz[N],son[N],top[N];
long long ans;
inline void add(int x,int y){ver[++tot]=y;nxt[tot]=fst[x];fst[x]=tot;}
void dfs1(int x,int F,int dep){
d[x]=dep;fa[x]=F;sz[x]=1;
int mx=0;
for(int i=fst[x];i;i=nxt[i]){
int y=ver[i];if(y==F)continue;
dfs1(y,x,dep+1);sz[x]+=sz[y];
if(sz[y]>sz[mx])mx=y;
}
son[x]=mx;
}
void dfs2(int x,int F){
lt[x]=++dfn;
if(son[x])top[son[x]]=top[x],dfs2(son[x],x);
for(int i=fst[x];i;i=nxt[i]){
int y=ver[i];if(y==F||y==son[x])continue;
top[y]=y;dfs2(y,x);
}
rt[x]=dfn;
}
inline int LCA(int x,int y){
while(top[x]!=top[y]){
if(d[top[x]]>=d[top[y]])x=fa[top[x]];
else y=fa[top[y]];
}
return (d[x]>=d[y])?y:x;
}
inline bool pd(int x,int y){
if(lt[y]>=lt[x]&<[y]<=rt[x])return true;
return false;
}
int solve(int p,int l,int r,int mx){
if(!mx)return sum[p];
if(pd(a[l],mx))return sum[p];
if(pd(lc[p],mx)){
if(l==r){
if(pd(lc[p],mx))return 1;
return 0;
}
int mid=(l+r)>>1;
if(pd(lc[p<<1],mx))return sum[p]-sum[p<<1]+solve(p<<1,l,mid,mx);
return solve((p<<1)|1,mid+1,r,LCA(lc[p<<1],mx));
}
return 0;
}
void build(int p,int l,int r){
if(l==r){
lc[p]=a[l];
sum[p]=1;
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);build((p<<1)|1,mid+1,r);
lc[p]=LCA(lc[p<<1],lc[(p<<1)|1]);
sum[p]=sum[p<<1];
sum[p]+=solve((p<<1)|1,mid+1,r,lc[p<<1]);
// cout<<"FAQ "<<l<<" "<<r<<" "<<sum[p]<<endl;
}
inline void cale(int p,int l,int r,int L,int R){
if(l>=L&&r<=R){
//
ans+=solve(p,l,r,zx);
// cout<<"CALE "<<l<<" "<<r<<" "<<zx<<" "<<solve(p,l,r,zx)<<endl;
if(zx)zx=LCA(zx,lc[p]);
else zx=lc[p];
return;
}
int mid=(l+r)>>1;
if(L<=mid)cale(p<<1,l,mid,L,R);
if(R>mid)cale((p<<1)|1,mid+1,r,L,R);
}
int main(){
// freopen("tree2-5 (1).in","r",stdin);
// freopen("tree.out","w",stdout);
n=read();m=read();q=read();
for(int i=1;i<n;++i){
int x=read(),y=read();
add(x,y);add(y,x);
}
for(int i=1;i<=m;++i)a[i]=read();
dfs1(1,0,1);top[1]=1;dfs2(1,0);
build(1,1,m);
while(q--){
int k=read();
zx=0;ans=0;
while(k--){
int L=read(),R=read();
cale(1,1,m,L,R);
}
printf("%lld\n",ans);
}
return 0;
}