【LNOI2014】【BZOJ3626】NOIp2018模拟(三) LCA

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设$dep[i]$表示点i的深度,$lca(i,j)$表示i与j的最近公共祖先。
有q次询问,每次询问给出l,r,z,求$\sum\limits_{i=l}^{r}dep[lca(i,z)]$。
(即求在$[l,r]$区间内的每个节点i与z的最近公共祖先的深度之和)

$n,q<=50000$

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

 

这题不算很难,但是看到正解的方法很有趣,就记录一下~

考虑暴力,每次询问把z到根的所有节点打上标记,枚举i的时候直接往根找第一个有标记的点,然后统计深度即可。

然而复杂度明显是大于$O(n^2q)$的,显然不能接受,容易发现如果把深度看成点权,那么统计深度就相当于把z到根的每个节点权值都加一,然后枚举时统计根节点到i的权值和。使用树链剖分可以降到$O(nqlog^2n)$,但是还是会超时。

考虑进一步优化,发现询问可以差分,拆成$[1,l-1]$和$[1,r]$两个询问,进一步可以发现对答案有贡献的点只会在$lca(i,z)$以上,因此把每个i到根路径上的结点权值加一,再统计z到根节点的权值和,得出的答案是相同的。再结合差分,每次直接将$[1,l-1]$或$[1,r]$中所有节点到根节点路径上的点权值加一,然后统计z到根节点的权值和,按照dfs序区间修改+区间查询,用树链剖分加线段树可以做到$O(qlog^2n)$,用LCT可以做到$O(qlogn)$

注意long long

代码:

 

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #define mod 201314
  7 using namespace std;
  8 typedef long long ll;
  9 struct edge{
 10     int v,next;
 11 }a[200001];
 12 struct task{
 13     int r,z,id,ok;
 14 }q[200001];
 15 int n,qq,u,v,z,tmp=0,qqq=0,tot=0,tim=0,head[100001],son[100001],siz[100001],fa[100001],dep[100001],dfn[100001],top[100001];
 16 ll t[500001],laz[500001],tv[500001],ans[100001],anss;
 17 bool cmp(task a,task b){
 18     return a.r<b.r;
 19 }
 20 void add(int u,int v){
 21     a[++tot].v=v;
 22     a[tot].next=head[u];
 23     head[u]=tot;
 24 }
 25 void dfs1(int u,int f,int dpt){
 26     dep[u]=dpt;
 27     fa[u]=f;
 28     siz[u]=1;
 29     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 30         int v=a[tmp].v;
 31         if(!dep[v]){
 32             dfs1(v,u,dpt+1);
 33             siz[u]+=siz[v];
 34             if(siz[v]>siz[son[u]]||son[u]==-1)son[u]=v;
 35         }
 36     }
 37 }
 38 void dfs2(int u,int tp){
 39     dfn[u]=++tim;
 40     top[u]=tp;
 41     if(son[u]!=-1)dfs2(son[u],tp);
 42     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 43         int v=a[tmp].v;
 44         if(v!=son[u])dfs2(v,v);
 45     }
 46 }
 47 void pushup(int u){
 48     t[u]=t[u*2]+t[u*2+1];
 49 }
 50 void pushdown(int u){
 51     if(laz[u]){
 52         laz[u*2]+=laz[u];
 53         laz[u*2+1]+=laz[u];
 54         t[u*2]+=tv[u*2]*laz[u];
 55         t[u*2+1]+=tv[u*2+1]*laz[u];
 56         laz[u]=0;
 57     }
 58 }
 59 void build(int l,int r,int u){
 60     if(l==r){
 61         tv[u]=1;
 62         return;
 63     }
 64     int mid=(l+r)/2;
 65     build(l,mid,u*2);
 66     build(mid+1,r,u*2+1);
 67     tv[u]=tv[u*2]+tv[u*2+1];
 68 }
 69 void updata(int l,int r,int u,int L,int R,int v){
 70     if(L<=l&&r<=R){
 71         t[u]+=(ll)tv[u]*v;
 72         laz[u]+=v;
 73         return;
 74     }
 75     int mid=(l+r)/2;
 76     pushdown(u);
 77     if(L<=mid)updata(l,mid,u*2,L,R,v);
 78     if(mid<R)updata(mid+1,r,u*2+1,L,R,v);
 79     pushup(u);
 80 }
 81 ll query(int l,int r,int u,int L,int R){
 82     if(L<=l&&r<=R){
 83         return t[u];
 84     }
 85     int mid=(l+r)/2,ans=0;
 86     pushdown(u);
 87     if(L<=mid)ans+=query(l,mid,u*2,L,R);
 88     if(R>mid)ans+=query(mid+1,r,u*2+1,L,R);
 89     return ans;
 90 }
 91 void work(int u){
 92     while(u){
 93         int v=top[u];
 94         updata(1,n,1,dfn[v],dfn[u],1);
 95         u=fa[v];
 96     }
 97 }
 98 ll _work(int u){
 99     ll ans=0;
100     while(u){
101         int v=top[u];
102         ans+=query(1,n,1,dfn[v],dfn[u]);
103         u=fa[v];
104     }
105     return ans;
106 }
107 int main(){
108     memset(son,-1,sizeof(son));
109     memset(head,-1,sizeof(head));
110     scanf("%d%d",&n,&qq);
111     for(int i=1;i<n;i++){
112         scanf("%d",&u);
113         add(u+1,i+1);
114     }
115     dfs1(1,0,1);
116     dfs2(1,1);
117     build(1,n,1);
118     for(int i=1;i<=qq;i++){
119         scanf("%d%d%d",&u,&v,&z);
120         u++,v++,z++;
121         q[++qqq]=(task){u-1,z,i,-1};
122         q[++qqq]=(task){v,z,i,1};
123     }
124     sort(q+1,q+qqq+1,cmp);
125     for(int i=1;i<=qqq;i++){
126         while(tmp<q[i].r){
127             work(++tmp);
128         }
129         ans[q[i].id]+=(ll)q[i].ok*_work(q[i].z);
130     }
131     for(int i=1;i<=qq;i++){
132         printf("%lld\n",ans[i]%mod);
133     }
134     return 0;
135 }

 

 

 

posted @ 2018-07-05 14:44  DCDCBigBig  阅读(177)  评论(0编辑  收藏  举报