[LNOI2014]LCA 树链剖分 离线 前缀和 思维题
题目描述:
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。 有q次询问,每次询问给出l r z,求
$\sum_{l\leq i\leq r} dep[LCA(i,z)]$
题解:
$1$. 这个问题看起来很麻烦,我们不难发现 $dep[LCA(i,z)]\geq dep[z]$,这是显然的。
$2$. 而我们还会发现我们要求的结果可以转化为将 $z$ 到根节点的边权全部置为 1,$l<=i<=r$ 中每个点到根节点的路径和。
$3$. 下一步,我们有不难看出这个操作是可逆的。即我们可以将 $l\leq i\leq r$ 中所有点到根节点之间的边权都 $+1$,求$z$到根节点的路径和。
$4$. 还可以看出,对于同一个 $t$,答案可以转化为 $ans(1,r)-ans(1,l-1)$。
综上,我们可以将每个询问拆成2个询问(分别询问 $l-1$ 和 $r$),并按照下标进行排序。
一边更新一边进行查询即可。
Code:
#include<cstdio> #include<algorithm> #include<string> #include<iostream> #include<cstring> using namespace std; void setIO(string a){ freopen((a+".in").c_str(),"r",stdin); } const int maxn=200000; const int mod=201314; int n,q; int dep[maxn],top[maxn],son[maxn],A[maxn],fa[maxn]; struct Get_Tree{ int cnt,cnt2; int nex[maxn],to[maxn],head[maxn],siz[maxn]; void add_edge(int u,int v){ nex[++cnt]=head[u]; head[u]=cnt; to[cnt]=v; } void dfs1(int u,int depth){ dep[u]=depth; siz[u]=1; for(int v=head[u];v;v=nex[v]){ dfs1(to[v],depth+1); siz[u]+=siz[to[v]]; if(!son[u]||siz[to[v]]>siz[son[u]]) son[u]=to[v]; } } void dfs2(int u,int tp){ top[u]=tp; A[u]=++cnt2; if(son[u])dfs2(son[u],tp); for(int v=head[u];v;v=nex[v]) if(to[v]!=son[u])dfs2(to[v],to[v]); } void solve(){ dfs1(1,1); dfs2(1,1); } }tree; struct Segment_Tree{ #define lson (o<<1) #define rson (o<<1)|1 int sumv[maxn<<2],lazy[maxn<<2]; void pushdown(int l,int r,int o){ if(lazy[o]){ int mid=(l+r)>>1; sumv[lson]+=(mid-l+1)*lazy[o]; sumv[rson]+=(r-mid)*lazy[o]; lazy[lson]+=lazy[o]; lazy[rson]+=lazy[o]; sumv[lson]%=mod; sumv[rson]%=mod; lazy[lson]%=mod; lazy[rson]%=mod; lazy[o]=0; } } void pushup(int o){ sumv[o]=sumv[lson]+sumv[rson]; } void update(int l,int r,int L,int R,int k,int o){ if(l>r||l>R||r<L)return; if(l>=L && r<=R){ sumv[o]+=(r-l+1)*k; lazy[o]+=k; sumv[o]%=mod; lazy[o]%=mod; return; } int mid=(l+r)>>1; pushdown(l,r,o); update(l,mid,L,R,k,lson); update(mid+1,r,L,R,k,rson); pushup(o); } int query(int l,int r,int L,int R,int o){ if(l>r||l>R||r<L)return 0; if(l>=L&&r<=R) return sumv[o]; int mid=(l+r)>>1; pushdown(l,r,o); return (long long)(query(l,mid,L,R,lson)+query(mid+1,r,L,R,rson))%mod; } int look_up(int x){ int val=0; while(x){ val+=query(1,n,A[top[x]],A[x],1); val%=mod; x=fa[top[x]]; } return val; } void modify(int x){ while(x){ update(1,n,A[top[x]],A[x],1,1); x=fa[top[x]]; } } }T; struct Ask{ int pos,idx,node,o; Ask(int pos=0,int idx=0,int node=0,int o=0):pos(pos),idx(idx),node(node),o(o){} }ask[maxn]; int asks=0; void Read(){ int f; scanf("%d%d",&n,&q); for(int i=2;i<=n;++i){ scanf("%d",&f); fa[i]=f+1; tree.add_edge(f+1,i); } for(int i=1;i<=q;++i){ int l,r,z; scanf("%d%d%d",&l,&r,&z); ask[++asks]=Ask(l-1+1,i,z+1,-1); ask[++asks]=Ask(r+1,i,z+1,1); } } int fin[maxn]; bool cmp(Ask i,Ask j){ return i.pos<j.pos; } void solve(){ tree.solve(); sort(ask+1,ask+1+asks,cmp); int cur=0; for(int i=1;i<=asks;++i){ while(cur<ask[i].pos){ T.modify(cur+1); cur+=1; } fin[ask[i].idx]+=T.look_up(ask[i].node)*ask[i].o; } for(int i=1;i<=q;++i)printf("%d\n",(fin[i]%mod+mod)%mod); } int main(){ Read(); solve(); return 0; }