题意: 给你一棵无向的树,然后给你这棵树的节点访问次序,起点任意,求每个节点的访问次数.
方法: 离线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) ; } }