生活随笔
收集整理的這篇文章主要介紹了
P3258 [JLOI2014]松鼠的新家(树上点查分)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目描述
松鼠的新家是一棵樹,前幾天剛剛裝修了新家,新家有n個房間,并且有n-1根樹枝連接,每個房間都可以相互到達,且倆個房間之間的路線都是唯一的。天哪,他居然真的住在”樹“上。
松鼠想邀請小熊維尼前來參觀,并且還指定一份參觀指南,他希望維尼能夠按照他的指南順序,先去a1,再去a2,…,最后到an,去參觀新家。可是這樣會導致維尼重復走很多房間,懶惰的維尼不停地推辭。可是松鼠告訴他,每走到一個房間,他就可以從房間拿一塊糖果吃。
維尼是個饞家伙,立馬就答應了。現在松鼠希望知道為了保證維尼有糖果吃,他需要在每一個房間各放至少多少個糖果。
因為松鼠參觀指南上的最后一個房間an是餐廳,餐廳里他準備了豐盛的大餐,所以當維尼在參觀的最后到達餐廳時就不需要再拿糖果吃了。
輸入格式
第一行一個整數n,表示房間個數第二行n個整數,依次描述a1-an
接下來n-1行,每行兩個整數x,y,表示標號x和y的兩個房間之間有樹枝相連。
輸出格式
一共n行,第i行輸出標號為i的房間至少需要放多少個糖果,才能讓維尼有糖果吃。
輸入輸出樣例
輸入 #1 復制
5
1 4 5 3 2
1 2
2 4
2 3
4 5
輸出 #1 復制
1
2
1
2
1
說明/提示
2<= n <=300000
關于點的差分(如將路徑上的所有點權值加一,求最后點的權值)
此操作中我們這樣維護:每次經過一條邊,(如從u到v)我們讓tmp[u]++,tmp[v]++,tmp[LCA(u,v)]–,tmp[grand[LCA(u,v)][0]]–。(最后要把tmp推上去)
以一次添加為例想象一下,首先u到根的路徑上tmp都+1,此時u到根間結點tmp都為1,之后v到根路徑上tmp+1,此時u到LCA前一個,v到LCA前一個點的tmp都+1,而LCA到根的所有點都+2,然后從tmp[LCA]–,更新上去,此時u-v路上所有tmp都+1,已經達到目的。
而多余的是什么部分呢,也就是LCA的上一個結點(grand[LCA][0])到根的這一段都多加了1,所以tmp[grand[LCA][0]]–,更新上去,也就完成了。
實際操作時也不需要每次更新都推上去,只要把四個tmp維護好,最后Dfs走一邊就更新完了。
松鼠的新家是樹上點差分模板題,代碼如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std
;const int maxx
=3e5+100;
struct edge
{int to
,next
;
}e
[maxx
<<1];
int head
[maxx
<<1],a
[maxx
],dp
[maxx
][26],deep
[maxx
],sum
[maxx
];
int n
,tot
;
inline void init()
{for(int i
=0;i
<=n
;i
++)for(int j
=0;j
<=20;j
++) dp
[i
][j
]=0;memset(sum
,0,sizeof(sum
));memset(head
,-1,sizeof(head
));tot
=0;
}
inline void add(int u
,int v
)
{e
[tot
].to
=v
,e
[tot
].next
=head
[u
],head
[u
]=tot
++;
}
inline void dfs(int u
,int f
)
{deep
[u
]=deep
[f
]+1;dp
[u
][0]=f
;for(int i
=1;(1<<i
)<=deep
[u
];i
++)dp
[u
][i
]=dp
[dp
[u
][i
-1]][i
-1];for(int i
=head
[u
];i
!=-1;i
=e
[i
].next
){int to
=e
[i
].to
;if(to
==f
) continue;dfs(to
,u
);}
}
inline void dfs2(int u
,int f
)
{for(int i
=head
[u
];i
!=-1;i
=e
[i
].next
){int to
=e
[i
].to
;if(to
==f
) continue;dfs2(to
,u
);sum
[u
]+=sum
[to
];}
}
int lca(int a
,int b
)
{if(deep
[a
]>deep
[b
]) swap(a
,b
);for(int i
=20;i
>=0;i
--){if(deep
[a
]<=deep
[b
]-(1<<i
)){b
=dp
[b
][i
];}}if(a
==b
) return a
;for(int i
=20;i
>=0;i
--){if(dp
[a
][i
]!=dp
[b
][i
])a
=dp
[a
][i
],b
=dp
[b
][i
];}return dp
[a
][0];
}
int main()
{int x
,y
;while(~scanf("%d",&n
)){init();for(int i
=1;i
<=n
;i
++) scanf("%d",&a
[i
]);for(int i
=1;i
<n
;i
++){scanf("%d%d",&x
,&y
);add(x
,y
);add(y
,x
);}deep
[0]=0;dfs(1,0);int f
;for(int i
=1;i
<n
;i
++){f
=lca(a
[i
],a
[i
+1]);sum
[a
[i
]]++;sum
[a
[i
+1]]++;sum
[f
]--;sum
[dp
[f
][0]]--;}dfs2(1,0);for(int i
=2;i
<=n
;i
++) sum
[a
[i
]]--;for(int i
=1;i
<=n
;i
++) printf("%d\n",sum
[i
]);}
}
努力加油a啊,(o)/~
總結
以上是生活随笔為你收集整理的P3258 [JLOI2014]松鼠的新家(树上点查分)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。