1. 程式人生 > >【JLOI 2014】松鼠的新家

【JLOI 2014】松鼠的新家

【題目】

傳送門

題目描述:

松鼠的新家是一棵樹,前幾天剛剛裝修了新家,新家有 n n 個房間,並且有 n 1 n-1

根樹枝連線,每個房間都可以相互到達,且任意兩個房間之間的路線都是唯一的。天哪,他居然真的住在「樹」上。

松鼠想邀請小熊維尼前來參觀,並且還指定一份參觀指南,他希望維尼能夠按照他的指南順序,先去 a 1 a_1 ,再去 a

2 a_2 ,…,最後到 a n a_n ,去參觀新家。

可是這樣會導致維尼重複走很多房間,懶惰的維尼不停地推辭。可是松鼠告訴他,每走到一個房間,他就可以從房間拿一塊糖果吃。維尼是個饞傢伙,立馬就答應了。現在松鼠希望知道為了保證維尼有糖果吃,他需要在每一個房間各放至少多少個糖果。

因為松鼠參觀指南上的最後一個房間 a n a_n 是餐廳,餐廳裡他準備了豐盛的大餐,所以當維尼在參觀的最後到達餐廳時就不需要再拿糖果吃了。

輸入格式:

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

第二行, n n 個整數,依次描述 a 1 a_1 a n a_n 。 接下來 n 1 n−1 行,每行兩個整數 x x y y ,表示標號 x x y y 的兩個房間之間有樹枝相連。

輸出格式:

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

樣例資料:

輸入
5
1 4 5 3 2
1 2
2 4
2 3
4 5

輸出
1
2
1
2
1

備註:

【資料範圍與提示】
對於所有資料, 2 2 n n 300000 300000


【分析】

題解:樹鏈剖分+線段樹

我們根據題目中給出的房間 a a 的順序,每次從 a i a_i 走到 a i + 1 a_{i+1} 時,就把 a i a_i a i + 1 a_{i+1} 路徑上的糖果加 1 1 a i a_i 這個點不加)

還有一些細節操作就是按照上面的操作下來 a 1 a_1 少算了一次, a n a_n 多算了一次,要特殊處理一下

然後其餘的就相當於是樹鏈剖分模板了


【程式碼】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define M 600005
using namespace std;
int n,m,t,tot;
int first[N],v[M],next[M],add[N<<2],sum[N<<2];
int a[N],dep[N],son[N],size[N],top[N],pos[N],father[N];
void edge(int x,int y)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void point(int x,int y)
{
	top[x]=y;
	pos[x]=++tot;
}
void dfs1(int x)
{
	int i,j;
	size[x]=1;
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j!=father[x])
		{
			father[j]=x;
			dep[j]=dep[x]+1;
			dfs1(j);
			size[x]+=size[j];
			if(size[son[x]]<size[j])
			  son[x]=j;
		}
	}
}
void dfs2(int x)
{
	int i,j;
	if(son[x])
	{
		point(son[x],top[x]);
		dfs2(son[x]);
	}
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j!=son[x]&&j!=father[x])
		{
			point(j,j);
			dfs2(j);
		}
	}
}
void pushdown(int root,int l,int r,int mid)
{
	add[root<<1]+=add[root];
	add[root<<1|1]+=add[root];
	sum[root<<1]+=add[root]*(mid-l+1);
	sum[root<<1|1]+=add[root]*(r-mid);
	add[root]=0;
}
void modify(int root,int l,int r,int x,int y,int k)
{
	if(l>=x&&r<=y)
	{
		add[root]+=k;
		sum[root]+=k*(r-l+1);
		return;
	}
	int mid=(l+r)>>1;
	if(add[root])  pushdown(root,l,r,mid);
	if(x<=mid)  modify(root<<1,l,mid,x,y,k);
	if(y>mid)  modify(root<<1|1,mid+1,r,x,y,k);
	sum[root]=sum[root<<1]+sum[root<<1|1];
}
void revise(int x,int y)
{
	modify(1,1,n,pos[x],pos[x],-1);
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])  swap(x,y);
		modify(1,1,n,pos[top[x]],pos[x],1);
		x=father[top[x]];
	}
	if(dep[x]>dep[y])  swap(x,y);
	modify(1,1,n,pos[x],pos[y],1);
}
int find(int root,int l,int r,int x)
{
	if(l==r)
	  return sum[root];
	int mid=(l+r)>>1;
	if(add[root])  pushdown(root,l,r,mid);
	if(x<=mid)  return find(root<<1,l,mid,x);
	return find(root<<1|1,mid+1,r,x);
}
int main()
{
	int x,y,i;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	for(i=1;i<n;++i)
	{
		scanf("%d%d",&x,&y);
		edge(x,y),edge(y,x);
	}
	point(1,1);
	dfs1(1);
	dfs2(1);
	modify(1,1,n,pos[a[1]],pos[a[1]],1);
	modify(1,1,n,pos[a[n]],pos[a[n]],-1);
	for(i=1;i<n;++i)  revise(a[i],a[i+1]);
	for(i=1;i<=n;++i)  printf("%d\n",find(1,1,n,pos[i]));
	return 0;
}