1. 程式人生 > >洛谷P3258 [JLOI2014]松鼠的新家——題解

洛谷P3258 [JLOI2014]松鼠的新家——題解

題目傳送門
題目大意:
給出一個在樹上移動的序列,求出每個點被經過的次數。

思考過程:
維護兩個點之間的鏈的資訊很明顯我們需要樹剖,但是樹剖無法維護鏈上每個節點的資訊,所以我們需要藉助差分陣列。

具體做法:
1.樹剖
2.對於序列中相鄰的兩個點,像普通樹剖求答案那樣往上跳到LCA,過程中將兩個節點的差分陣列起點+1,終點-1,注意重複的情況,用tag陣列來記錄

程式碼:

#include <bits/stdc++.h>
using namespace std;

const int maxn=3e5+1000;
struct stu
{
    int to,next;
}road[maxn*2
]; int first[maxn],cnt=0; int dep[maxn],cnum[maxn],id[maxn],dfn[maxn],top[maxn],fa[maxn],hson[maxn],cf[maxn],sum[maxn],tag[maxn],a[maxn]; int n,nowtop; void addedge(int x,int y) { road[++cnt].to=y; road[cnt].next=first[x]; first[x]=cnt; } void dfs1(int now,int depth) { int maxx=0; dep[now]=depth; cnum[now]=1
; for(int i=first[now];i;i=road[i].next) { int to=road[i].to; if(to==fa[now]) continue; fa[to]=now; dfs1(to,depth+1); cnum[now]+=cnum[to]; if(cnum[to]>maxx) { maxx=cnum[to]; hson[now]=to; } } } void
dfs2(int now,bool what) { if(what==1) nowtop=now; top[now]=nowtop; id[now]=++cnt; dfn[cnt]=now; if(!hson[now]) return; dfs2(hson[now],0); for(int i=first[now];i;i=road[i].next) { int to=road[i].to; if(to==fa[now]||to==hson[now]) continue; dfs2(to,1); } } void calcu(int x,int y) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); cf[id[top[x]]]++;cf[id[x]]--; tag[x]++; x=fa[top[x]]; } if(dep[x]<dep[y]) swap(x,y); cf[id[y]]++;cf[id[x]]--; tag[x]++; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n-1;i++) { int x,y; scanf("%d%d",&x,&y); addedge(x,y);addedge(y,x); } dfs1(a[1],1); cnt=0; dfs2(a[1],1); calcu(a[1],a[2]); for(int i=2;i<=n-1;i++) { calcu(a[i],a[i+1]); tag[a[i]]--; } tag[a[n]]--; sum[1]=cf[1]; for(int i=2;i<=n;i++) sum[i]=sum[i-1]+cf[i]; for(int i=1;i<=n;i++) printf("%d\n",sum[id[i]]+tag[i]); return 0; }