1. 程式人生 > >【BZOJ3631/JLOI2014】松鼠的新家

【BZOJ3631/JLOI2014】松鼠的新家

                                     3631: [JLOI2014]松鼠的新家

                                                        Time Limit: 10 Sec  Memory Limit: 128 MB                                                                     Submit: 3084  Solved: 1681

Description

松鼠的新家是一棵樹,前幾天剛剛裝修了新家,新家有n個房間,並且有n-1根樹枝連線,每個房間都可以相互到達,且倆個房間之間的路線都是唯一的。天哪,他居然真的住在“樹”上。松鼠想邀請小熊維尼前來參觀,並且還指定一份參觀指南,他希望維尼能夠按照他的指南順序,先去a1,再去a2,……,最後到an,去參觀新家。

可是這樣會導致維尼重複走很多房間,懶惰的維尼不聽地推辭。可是松鼠告訴他,每走到一個房間,他就可以從房間拿一塊糖果吃。維尼是個饞傢伙,立馬就答應了。

現在松鼠希望知道為了保證維尼有糖果吃,他需要在每一個房間各放至少多少個糖果。因為松鼠參觀指南上的最後一個房間an是餐廳,餐廳裡他準備了豐盛的大餐,所以當維尼在參觀的最後到達餐廳時就不需要再拿糖果吃了。

Input

第一行一個整數n,表示房間個數

第二行n個整數,依次描述a1-an

接下來n-1行,每行兩個整數x,y,表示標號x和y的兩個房間之間有樹枝相連。

Output

一共n行,第i行輸出標號為i的房間至少需要放多少個糖果,才能讓維尼有糖果吃。

Sample Input

5 1 4 5 3 2 1 2 2 4 2 3 4 5

Sample Output

1 2 1 2 1

HINT

2<= n <=300000

解析:

       樹上差分(當然硬上樹鏈剖分也是沒問題的。。。)。

       簡化題目後發現只需要維護 u 到 v 路徑上點權加一以及求點權,那麼就可以用差分的方法解決。

       對於 u 到 v 路徑上點權加一的操作,只需要在 u 和 v 打一個加一標記,在LCA[u,v] 和 fa[LCA(u,v)] 打一個減一標記,這樣一個點的子樹的和即為這個點的實際值。

       感性理解一下就是在LCA[u,v]減一是為了重複算,在fa[LCA(u,v)] 打一個減一標記是為了防止對LCA(u,v)產生影響。

程式碼:

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

const int Max=300005;
int n,m,s;
int first[Max],sum[Max],a[Max],top[Max],fa[Max],dep[Max],son[Max],size[Max];
struct shu{int to,next;}edge[Max<<1];

inline int get_int()
{
	int x=0,f=1;char c;
	for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
	if(c=='-') f=-1,c=getchar();
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	return x*f;
}
inline void print(int x)
{
	if(x>9) print(x/10);
	putchar('0'+x%10);
}

inline void build(int x,int y){edge[++s].next=first[x],first[x]=s,edge[s].to=y;}
inline void dfs1(int p)
{
	size[p]=1;
	for(int u=first[p];u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(to==fa[p]) continue;
	  fa[to]=p,dep[to]=dep[p]+1,dfs1(to),size[p]+=size[to];
	  if(size[to]>size[son[p]]) son[p]=to;
	}
}
inline void dfs2(int p,int tp)
{
	top[p]=tp;
	if(!son[p]) return;
	dfs2(son[p],tp);
	for(int u=first[p];u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(to==fa[p]||to==son[p]) continue;
	  dfs2(to,to);
	}
}
inline void dfs3(int p)
{
	for(int u=first[p];u;u=edge[u].next)
	{
	  int to=edge[u].to;
	  if(to==fa[p]) continue;
	  dfs3(to);
	  sum[p]+=sum[to];
	}
}
inline int LCA(int x,int y)
{
	while(top[x]^top[y])
	{
	  if(dep[top[x]]<dep[top[y]]) swap(x,y);
	  x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

inline void init()
{
	n=get_int();
	for(int i=1;i<=n;i++) a[i]=get_int();
	for(int i=1;i<n;i++)
	{
	  int x=get_int(),y=get_int();
	  build(x,y),build(y,x);
	}
}
inline void solve()
{
	dfs1(1),dfs2(1,1);
	for(int i=1;i<n;i++)
	{
	  int father=LCA(a[i],a[i+1]);
	  sum[a[i]]++,sum[a[i+1]]++,sum[father]--,sum[fa[father]]--;
	}
	dfs3(1);
	for(int i=2;i<=n;i++) sum[a[i]]--;
	for(int i=1;i<=n;i++) print(sum[i]),putchar('\n');
}

int main()
{
	init();
	solve();
	return 0;
}