[支配树][lca][倍增][线段树][拓扑] Jzoj P4240 游行

Description

恶梦是学校里面的学生会主席。他今天非常的兴奋,因为学校一年一度的学生节开始啦!!
在这次节日上总共有N个节目,并且总共也有N个舞台供大家表演。其中第i个节目的表演时间为第i个单位时间,表演的舞台为Ai,注意可能有多个节目使用同一个舞台。
作为恶梦的忠实粉丝之一的肥佬,当然要来逛一下啦,顺便看一下能不能要到恶梦的签名。
肥佬一开始会先在A1 看完节目1再去闲逛。
肥佬可以在舞台之间随便乱走。但是假如肥佬当前在看第i个节目,站在第Ai个舞台前面的话,由于有些道路被封锁了,所以肥佬下一步只能前往第Li到第Ri个舞台中的一个。并且当一个节目结束的时候,肥佬只能去看另外一个节目,或者结束自己的闲逛。
具体而言就是说,假设肥佬可以从第i个节目走去第j个节目,那么当且仅当i<j且Li <= Aj <= Ri。
但事实上是,恶梦非常讨厌被自己的粉丝跟踪。所以他想在只封锁掉一个节目的情况下,使得肥佬不能到达自己所在的地方。并且为了防止意外,他还想知道有多少个这样的节目。
简而言之,恶梦想知道对于任意一个节目p∈[1,N],有多少个节目t,使得删掉t之后,不存在一条从节目1出发到节目p的路径。注意,节目1和节目p也是可以被删的。
由于他非常的忙碌,所以他把这个任务交给了你。
 

Input

第一行包括一个正整数N,表示总共有N个节目。
第二行包括N个正整数Ai,表示第i个节目占用了第Ai个舞台。
接下来的N行,第i行包括两个正整数Li,Ri,表示第i个节目的路径限制。

Output

总共N行。第i行包括一个整数c,表示当恶梦站在第i个节目的时候,有多少个满足要求的节点。
特别的,若一开始就不存在从1出发到i的路径的话,你需要输出-1.
 

Sample Input

10
1 6 1 8 7 2 3 9 10 10
5 8
2 4
1 2
9 10
8 9
9 9
10 10
2 2
2 4
9 9

Sample Output

1
2
-1
2
2
3
3
2
2
2
 

Data Constraint

对于15%的数据,N <= 100
对于30%的数据,N <= 800
对于50%的数据,N <= 5000
对于70%的数据,N <= 10000
对于100%的数据,N <= 50000
 

Hint

样例解释:
我们假如将一个节目视为一个节点的话,按题意所述,我们可以构造出一副有向图。

设对于点i,他可选的删除集合为Si
那么很直观的就可以看出来:
对于1号节点,S1 = {1}
对于2号节点,S2 = {1,2}
对于3号节点,由于本来就不存在1到3的路径,所以应输出-1
对于4号节点,S4 = {1,4}
对于5号节点,S5 = {1,5}
对于6号节点,S6 = {1,2,6}
对于7号节点,S7 = {1,2,7}
对于8号节点,S8 = {1,8},5号点和6号点都不是合法的点。
对于9号节点,S9 = {1,9}
对于10号节点,S10 = {1,10}

题解

  • 题目大意:给定一个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 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/10336167.html
今日推荐