BZOJ 3631 [JLOI2014]松鼠的新家 tarjanlca

来源:互联网 发布:iphone铃声助手软件 编辑:程序博客网 时间:2024/04/29 17:31

题意: 给你一棵无向的树,然后给你这棵树的节点访问次序,起点任意,求每个节点的访问次数.

方法: 离线tarjan lca.

解析: (果然自己还是弱啊,结尾标记都不会传) , 膜拜神犇orz PoPoQQQ

       首先题意说的已经很清了,用lca就可以过.

 用sum数组记录首标记,end记录尾标记,在深搜里上传就可以.

自己说下对这个代码部分地方的理解吧,

首先,为什么end标记要给两个点的lca也记录: 因为两个点在传上sum的时候,到了lca这个交叉点的时候,会多出来一个标记,所以end要给lca也记录.

其次,结尾输出为什么除了第一个点其它的sum都要减一: 因为每次访问是不包括起始点的,在深搜的时候,因为要上传,所以给起始点也加上1,这就造成了多了一个标记,所以输出的时候减去.

再次膜拜神犇orz PoPoQQQ

#include <stdio.h>#include <string.h>struct node{int to ;int next ;};node edge[600001] ;node edge2[600001] ;int head2[300001] ;int head[300001] ;int deep[300001] ;int fa[300001] ;int f[300001] ;int sum[300001] ;int end[300001] ;int a[300001] ;int v[300001] ;int cnt , cnt2;void init(){memset(head , -1 , sizeof(head)) ;memset(head2 , -1 , sizeof(head2)) ;cnt = 1 , cnt2 = 1 ;}void edgeadd(int from , int to){edge[cnt].to = to ;edge[cnt].next = head[from] ;head[from] = cnt ++ ;}void edgeadd2(int from , int to){edge2[cnt2].to = to ;edge2[cnt2].next = head2[from] ;head2[from] = cnt2 ++ ;}int find(int x){if(f[x] == x) return x ;else {f[x] = find(f[x]) ;return f[x] ;}}void tarjan(int x){for(int i = head[x] ; i != -1 ; i = edge[i].next){int to = edge[i].to ;if(to != fa[x]){fa[to] = x , tarjan(to) , f[to] = x ;}}v[x] = 1 ;for(int i = head2[x] ; i != -1 ; i = edge2[i].next){int to = edge2[i].to ;if(v[to]){end[find(to)] ++ , end[fa[find(to)]] ++ ;}}sum[x] -= end[x] ;sum[fa[x]] += sum[x] ;}int main(){int n ;scanf("%d" , &n) ;init() ;for(int i = 1 ; i <= n ; i++){f[i] = i ;scanf("%d" , &a[i]) ;if(i > 1){edgeadd2(a[i] , a[i-1]) ;edgeadd2(a[i-1] , a[i]) ;sum[a[i]] ++ ;sum[a[i-1]] ++ ;}}for(int i = 1 ; i <= n - 1 ; i++){int x , y ;scanf("%d%d" , &x , &y) ;edgeadd(x , y) ;edgeadd(y , x) ;}tarjan(1) ;for(int i = 1 ; i <= n ; i++){if(i == a[1]) printf("%d\n" , sum[i]) ;else printf("%d\n" , sum[i]-1) ;}}


0 0