JZOJ5944. 【NOIP2018模擬11.01】信標
阿新 • • 發佈:2018-11-05
題意:
資料範圍:
對於前
% 的資料,
;
對於前
% 的資料,
, 樹的形態隨機;
對於前
% 的資料,
;
對於另
% 的資料, 不存在一個村莊連線著
條或以上的道路;
對於
% 的資料,
,
, 保證資料合法.
Analysis:
又得上一堆結論,考場就只個推出放葉子節點最優,還是趕緊收拾收拾滾回家吧,考什麼NOIP。
首先假如以一個節點為根,放一個信標在上面,那麼限制只會在相同深度之間,相當於給樹分層了。
考慮怎麼放最優,如果我把一個信標放在一個分叉的上面,我可以把這個信標拖到分叉處,使得更多的節點被覆蓋,限制被解決,那麼最後肯定會有一條鏈往下,我們可以把信標拖到葉子節點處,這樣不會解決上下對稱,答案不會更劣,所以放葉子節點最優。
對於一個節點,它會成為若干深度相同節點的LCA,若一個信標不放到這些節點對所處子樹,那麼他們就會不合法(先走到LCA然後往下走)。對於一個點,我們就可以發現,假如他有
個兒子,那麼我們就要放
個信標在不同的兒子裡,使得覆蓋所有限制。
但這個策略需要列舉根,放信標在上面來分層,考慮在什麼情況下不在根放信標成立。
發現只要根的度數大於等於
即可。考慮深度不同的如何解決,若一對點處在同一子樹,顯然可以放一個信標在另一個子樹解決限制。若處在不同子樹,那麼該對點按以上策略,肯定有一個點子樹內有信標,我們貪心地將信標下放到了葉子節點,那麼肯定可以將其區分開。
那麼按照以上策略貪心即可。複雜度
。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 1e6 + 5;
int st[N],to[N << 1],nx[N << 1];
int rd[N],sz[N];
int n,tot,rt,ans;
inline void add(int u,int v)
{
to[++tot] = v,nx[tot] = st[u],st[u] = tot;
to[++tot] = u,nx[tot] = st[v],st[v] = tot;
}
inline void dfs(int x,int fr)
{
int cnt = 0;
for (int i = st[x] ; i ; i = nx[i])
if (to[i] != fr) dfs(to[i],x),++cnt;
if (cnt > 1) sz[x] = 1;
for (int i = st[x] ; i ; i = nx[i])
if (to[i] != fr && sz[to[i]]) --cnt,sz[x] = 1;
if (cnt) ans += cnt - 1;
}
int main()
{
freopen("beacon.in","r",stdin);
freopen("beacon.out","w",stdout);
scanf("%d",&n);
if (n == 1) { puts("0"); return 0; }
for (int i = 1 ; i < n ; ++i)
{
int u,v; scanf("%d%d",&u,&v);
add(u,v),++rd[u],++rd[v];
}
for (int i = 1 ; i <= n ; ++i) if (rd[i] > 2) rt = i;
if (!rt) { puts("1"); return 0; }
dfs(rt,0);
printf("%d\n",ans);
return 0;
}