USACO Cow at Large
USACO Cow at Large
題目描述
最後,Bessie 被迫去了一個遠方的農場。這個農場包含 NN 個穀倉(2 \le N \le 10^52≤N≤105)和 N-1N−1 條連線兩個穀倉的雙向隧道,所以每兩個穀倉之間都有唯一的路徑。每個只與一條隧道相連的穀倉都是農場的出口。當早晨來臨的時候,Bessie 將在某個穀倉露面,然後試圖到達一個出口。
但當 Bessie 露面的時候,她的位置就會暴露。一些農民在那時將從不同的出口穀倉出發嘗試抓住 Bessie。農民和 Bessie 的移動速度相同(在每個單位時間內,每個農民都可以從一個穀倉移動到相鄰的一個穀倉,同時 Bessie 也可以這麼做)。農民們和Bessie 總是知道對方在哪裡。如果在任意時刻,某個農民和 Bessie 處於同一個穀倉或在穿過同一個隧道,農民就可以抓住 Bessie。反過來,如果 Bessie 在農民們抓住她之前到達一個出口穀倉,Bessie 就可以逃走。
Bessie 不確定她成功的機會,這取決於被僱傭的農民的數量。給定 Bessie 露面的穀倉K,幫助 Bessie 確定為了抓住她所需要的農民的最小數量。假定農民們會自己選擇最佳的方案來安排他們出發的出口穀倉。
輸入格式
輸入的第一行包含 NN 和 KK。接下來的 N – 1N–1 行,每行有兩個整數(在 1\sim N1∼N 範圍內)描述連線兩個穀倉的一條隧道。
輸出格式
輸出為了確保抓住 Bessie 所需的農民的最小數量。
由 @Marser 提供翻譯
題解:
2020.11.12模擬賽滿分場
%%%學弟TQL
一開始看到樹上最優化想樹形DP,後來發現推不出一個轉移方程。覺得是不是自己想假了?後來開始手玩樣例,發現一個性質:
按理講,每個葉子節點都應該安排人去堵,但是不必要,因為只要有多個人是等效的,就只派一個人去收即可。
什麼時候是等效的呢?
我們對於每個節點維護兩個資訊:第一個資訊是deep[x],牛最早能多早到達這個節點,其值就是深度-1。第二個資訊是low[x],最快到達這個節點的追的人最早能多早到達這個節點,其值可以深搜處理。很容易得出,如果low[x]<=deep[x],這個點就被堵死了,但是這個點的貢獻只是1,無論下面有多少葉子節點。
所以再深搜一遍,碰到堵死的節點就把ans++,再返回就可。
程式碼:
#include<cstdio> #include<algorithm> using namespace std; const int maxn=1e5+5; const int INF=1e9; int n,s; int tot,head[maxn],nxt[maxn<<1],to[maxn<<1]; int fa[maxn],deep[maxn],low[maxn]; int du[maxn]; int ans; void add(int x,int y) { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void dfs1(int x,int f) { deep[x]=deep[f]+1; fa[x]=f; int tmp=INF; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==f) continue; dfs1(y,x); tmp=min(tmp,low[y]); } if(du[x]==1) low[x]=0; else low[x]=tmp+1; } void dfs2(int x) { if(deep[x]>=low[x]) { ans++; return; } for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(y==fa[x]) continue; dfs2(y); } } int main() { scanf("%d%d",&n,&s); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); du[x]++,du[y]++; } deep[0]=-1; dfs1(s,0); dfs2(s); printf("%d\n",ans); return 0; }