[支配树][lca][倍增][线段树][拓扑] Jzoj P4240 游行
题解
- 题目大意:给定一个DAG,问从1到每个点必经点的个数
- 30%的数据,强行建图,强行跑,O(n^3)
- 我们先引入一个东东,叫支配树
-
支配树简单点来说其实就是求有向图中每个点的必经点个数的有效方法,这题中DAG是一个有向无环图,满足支配树的要求。设idom[i]表示从s到i最近的必经点,dom[i]表示从s到i的必经点的集合
那么对于每个点i,它的idom和dom都是唯一的,这样的话就会构成一棵树,这棵树就是所谓的Dominator tree,也就是支配树。dom[i]也就是i点在这棵树上所有祖先的集合
考虑如何求idom数组,然后我们可以发现,对于i点,只能有一个点到达,那么这个可以到达的点就一定是它的idom;若该点有多个点可以到达,那么其实就是这多个点的lca
然后,每个点的必经点的个数,其实就是在支配树中的深度(可以想想为什么)
这样的话,我们就能一遍拓扑排序,一边求lca,时间复杂度为O((N + E)log N), N为图中的点数,E为图中的边数 - 这个东东说完了,那么怎么做呢,先来看看75分滴
- 75%,对于这档数据,我们可以很暴力的去做,先记录下每个点的入度,然后拓扑排序,用倍增建边,直接跑lca就好了
- 但是这个东东,时间复杂度是过不去的,建边太暴力了,严重影响时间复杂度,那么我们就可以考虑用数据结构来优化建边,然后就有了100分的做法
- 100%,我们可以用线段树来维护每个点的idom(最近的必经点),然后用线段树的方式建边,也是倍增跑lca,最后本题的时间复杂度为O((NlogN)log (NlogN))
- 此题完结(撒花)^_^
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cmath> 4 #define N 50010 5 using namespace std; 6 int n,m,o,a[N],tree[N*3],f[N][20],dep[N]; 7 int LCA(int x,int y) 8 { 9 if (!x&&!y) return 0; if (!x||!y) return x+y; 10 if (dep[x]<dep[y]) swap(x,y); 11 if (dep[x]!=dep[y]) for (int i=log(n)/log(2);i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i]; 12 if (x==y) return x; 13 for (int i=log(n)/log(2);i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 14 return f[x][0]; 15 } 16 int getlca(int d,int l,int r,int k) 17 { 18 if (l==r) return tree[d]; 19 int mid=l+r>>1; 20 if (k<=mid) return LCA(tree[d],getlca(d*2,l,mid,k)); else return LCA(tree[d],getlca(d*2+1,mid+1,r,k)); 21 } 22 void change(int d,int l,int r,int L,int R,int k) 23 { 24 if (l==L&&r==R) { tree[d]=LCA(tree[d],k); return; } 25 int mid=l+r>>1; 26 if (R<=mid) change(d*2,l,mid,L,R,k); else if (L>mid) change(d*2+1,mid+1,r,L,R,k); else change(d*2,l,mid,L,mid,k),change(d*2+1,mid+1,r,mid+1,R,k); 27 } 28 int main() 29 { 30 scanf("%d",&n),dep[1]=1; 31 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 32 for (int i=1,l,r;i<=n;i++) 33 { 34 scanf("%d%d",&l,&r),o=1; 35 if (i>1) 36 { 37 int lca=getlca(1,1,n,a[i]); 38 if (lca) 39 { 40 f[i][0]=lca,dep[i]=dep[lca]+1; 41 for (int j=1;j<=log(n)/log(2);j++) f[i][j]=f[f[i][j-1]][j-1]; 42 } 43 else dep[i]=-1,o=0; 44 } 45 if (o) change(1,1,n,l,r,i); 46 printf("%d\n",dep[i]); 47 } 48 }