1. 程式人生 > 實用技巧 >【GMOJ6803】模擬tom

【GMOJ6803】模擬tom

題目

題目連結: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;
}