【GMOJ6803】模擬tom
阿新 • • 發佈:2020-10-14
題目
題目連結:https://gmoj.net/senior/#main/show/6803
眾所周知,Tom 貓對香腸非常感興趣。
有一天,Tom 家裡的女主人賞給了 Tom 一大堆香腸。這些香腸太多了,以至於 Tom 一頓吃不完,
於是它把這些香腸串成了一棵樹,樹的每個節點上都有一個香腸。
Tom 需要給這些香腸進行編號,其中有 a 個香腸需要編號為 1,2···a 中的不重複的編號,作為早餐
腸,剩下的 b 個香腸需要編號為 −1,−2··· − b 中的不重複的編號,作為晚餐腸。
Tom 每天會隨機吃一頓飯,可能是早飯,也可能是晚飯。如果是吃早飯,Tom 會吃掉編號絕對值最
小的早餐腸,反之吃掉編號絕對值最小的晚餐腸。
如果一根香腸被吃掉了,那麼與它相連的樹上的邊都會斷掉,因此剩下的香腸可能會因此變成若干
棵樹,即變得不再連通。這是 Tom 不希望發生的事。
請給這些香腸編號,使得無論 Tom 如何安排早飯和晚飯,整棵樹一直都是連通的。
思路
首先如果早餐腸和晚餐腸不分別成連通塊顯然不行,只要將其中一方去完不取另一方就可以分成多個連通塊。
否則按照拓撲序來編號一定有解。
而拆成兩個連通塊大小分別為 \(a\) 和 \(b\) 當且僅當存在一個子樹大小為 \(a\) 或 \(b\)。將這個子樹與其父親的邊斷掉分別編號即可。
時間複雜度 \(O(n)\)。
程式碼
#include <bits/stdc++.h> using namespace std; const int N=100010; int n,a,b,pos,last,tot=1,head[N],size[N],ans[N]; bool flag; struct edge { int next,to,from; }e[N*2]; void add(int from,int to) { e[++tot].to=to; e[tot].from=from; e[tot].next=head[from]; head[from]=tot; } void dfs1(int x,int fa,int id) { size[x]=1; for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa) { dfs1(v,x,i); size[x]+=size[v]; if (pos) return; } } if (size[x]==a || size[x]==b) pos=id; } void dfs2(int x,int fa,int v) { for (int i=head[x];~i;i=e[i].next) { int y=e[i].to; if (y!=fa) dfs2(y,x,v); } tot+=v; ans[x]=tot; last=tot; } int main() { freopen("tom.in","r",stdin); freopen("tom.out","w",stdout); memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&a,&b); for (int i=1,x,y;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs1(1,0,0); if (!pos) return printf("-1"),0; tot=0; dfs2(e[pos].from,e[pos].to,1); tot=0; dfs2(e[pos].to,e[pos].from,-1); for (int i=1;i<=n;i++) if (last==-b) printf("%d\n",ans[i]); else printf("%d\n",-ans[i]); return 0; }