[BZOJ3631][JLOI2014]松鼠的新家


題目描述 Description

松鼠的新家是一棵樹,前幾天剛剛裝修了新家,新家有n個房間,並且有n-1根樹枝連接,每個房間都可以相互到達,且倆個房間之間的路線都是唯一的。天哪,他居然真的住在“樹”上。松鼠想邀請小熊維尼前來參觀,並且還指定一份參觀指南,他希望維尼能夠按照他的指南順序,先去a1,再去a2,……,最后到an,去參觀新家。可是這樣會導致維尼重復走很多房間,懶惰的維尼不聽地推辭。可是松鼠告訴他,每走到一個房間,他就可以從房間拿一塊糖果吃。維尼是個饞家伙,立馬就答應了。現在松鼠希望知道為了保證維尼有糖果吃,他需要在每一個房間各放至少多少個糖果。因為松鼠參觀指南上的最后一個房間an是餐廳,餐廳里他准備了豐盛的大餐,所以當維尼在參觀的最后到達餐廳時就不需要再拿糖果吃了。

輸入描述 Input Description

第一行一個整數n,表示房間個數第二行n個整數,依次描述a1-an接下來n-1行,每行兩個整數x,y,表示標號x和y的兩個房間之間有樹枝相連。

輸出描述 Output Description

一共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

數據范圍及提示 Data Size & Hint

 2<= n <=300000

之前的一些廢話:會考結束,沒出成績,准備期末復習。說實話這道題看完之后發慌,因為。。有點像某道給我心理陰影面積極大的題(NOIP2016天天愛跑步

題解:在樹的路徑上進行修改,其實這道題是可以那樹鏈剖分+線段樹水過的。但是我們對於這種離線的,可以使用一種叫做樹上差分的技巧。還記得借教室那道題么?也是涉及到線性的區間修改,但是我們利用差分+前綴和的思想,不需要修改整個區間,只需要動區間兩端即可,最后前綴和掃一遍就能把所有區間打的標記算好。樹上的話也是類似,把a,b這段路徑和都加上1的方法:在a,b位置各加上1,然后自底向上過程中就能把a,b路徑上的點全部算入,然后匯聚到lca(a,b)的時候會算兩次,所以我們需要在lca(a,b)處減1,之后在lca(a,b)以上的部分就不會進行修改了,所以我們再在fa[lca(a,b)]處減一即可。自底向上的話,用類似樹形DP的方法來求即可。至於lca的部分,以后不玩倍增了,還是寫樹鏈剖分比較穩。

代碼:

#include<iostream>
#include
<cmath>
#include
<algorithm>
#include
<cstring>
#include
<queue>
#include
<cstdio>
using namespace std;
typedef
long long LL;
#define mem(a,b) memset(a,b,sizeof(a))
typedef pair
<int,int> PII;
inline
int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=300010;
struct Edge
{
int u,v,next;
Edge() {}
Edge(
int _1,int _2,int _3):u(_1),v(_2),next(_3) {}
}e[maxn
<<1];
int n,ce=-1,es,a,b,A[maxn],first[maxn],deep[maxn],fa[maxn],size[maxn],ms[maxn],top[maxn],tag[maxn];
void addEdge(int a,int b)
{
e[
++ce]=Edge(a,b,first[a]);first[a]=ce;
e[
++ce]=Edge(b,a,first[b]);first[b]=ce;
}
void dfs(int now,int pa)
{
size[now]
=1;
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].v!=pa)
{
fa[e[i].v]
=now;deep[e[i].v]=deep[now]+1;
dfs(e[i].v,now);
size[now]
+=size[e[i].v];
if(size[e[i].v]>size[ms[now]])ms[now]=e[i].v;
}
}
void divide(int now,int chain)
{
top[now]
=chain;
if(ms[now])divide(ms[now],chain);
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].v!=ms[now] && fa[now]!=e[i].v)divide(e[i].v,e[i].v);
}
int lca(int a,int b)
{
while(top[a]!=top[b])
{
if(deep[top[a]]<deep[top[b]])swap(a,b);
a
=fa[top[a]];
}
return deep[a]<deep[b] ? a : b;
}
void pushup(int now)
{
for(int i=first[now];i!=-1;i=e[i].next)
if(fa[now]!=e[i].v)pushup(e[i].v),tag[now]+=tag[e[i].v];
}
int main()
{
mem(first,
-1);
n
=read();
for(int i=1;i<=n;i++)A[i]=read();
for(int i=1;i<n;i++)a=read(),b=read(),addEdge(a,b);
dfs(
1,0);divide(1,1);
for(int i=1;i<n;i++)
{
int t=lca(A[i],A[i+1]);
tag[A[i]]
++;tag[A[i+1]]++;
tag[t]
--;tag[fa[t]]--;
}
pushup(
1);
for(int i=2;i<=n;i++)tag[A[i]]--;
for(int i=1;i<=n;i++)printf("%d\n",tag[i]);
return 0;
}
View Code

總結:樹上差分似乎近幾年很愛考,一定要牢記。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com