BZOJ 3631 [JLOI2014]松鼠的新家 tarjanlca

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

方法: 离线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) ;
	}
}


你可能感兴趣的:(LCA,Tarjan)