[USACO18DEC] The Cow Gathering P

题目传送门

思维题。
先转化题意,如果在朋友之间连边,显然构成一棵树。删点时只能找叶子节点删,要不然树一分裂最后肯定有人落单。除此之外还有一些形如\(x\)必须比\(y\)先删之类的限制。
首先想如何判断无解。尝试找一个合法点,如果找不到就无解了。
需要一种找合法点的方法,使得只要有解就一定能找到任意一个合法点。考虑如下过程:
每次找一个没有其它点限制他的叶子节点,把他踢了。
最后剩下那个点就是一个合法点。
找到一个合法点后,怎样快速找到其他合法点?
可以发现,以当前找到的合法点为根时(下面说的树都为此),对于限制\((u,v)\)\(u\)的子树内的点一定都不是合法点。
可以证明,剩下的点都是合法点。
首先,剩下的点一定在树上一定构成一个连通块,我们只要证明与当前合法点\(u\)相连的点(除了已经判断不合法的点)都是合法点,就可以进行推广。
考虑\(u\)的儿子\(v\)\(v\)既然未被标记非法,说明他不会限制其它点。也就是他最后删,不会有其它点因为受限制删不了。
那就让他最后删呗。原来的\(u\)倒数第二个删,其他删数顺序不变。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x

const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const ll INF = 1e18;

int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }

int n, m, rt;
int d[N], vis[N], res[N];
vector<int>l[N], g[N];
queue<int>q;

void dfs(int x, int fr){
	res[x] = 1;
	for(auto y : l[x]){
		if(vis[y] || y == fr) continue;//在y这就回去了,y的子树自然扫不到
		dfs(y, x);
	}
}

signed main(){
	n = read(), m = read();
	for(int i = 1; i < n; i++){
		int x = read(), y = read();
		l[x].pb(y), l[y].pb(x);
		d[x]++, d[y]++;
	}
	for(int i = 1; i <= m; i++){
		int x = read(), y = read();
		g[x].pb(y);
		d[y]++, vis[x] = 1;
	}
	for(int i = 1; i <= n; i++) if(d[i] == 1) q.push(i);
	int tt = 0;
	while(q.size()){
		int x = q.front();
		q.pop();
		tt++;
		if(d[x] <= 0){
			rt = x;
			break;
		}
		for(auto y : l[x]) if(--d[y] == 1) q.push(y);
		for(auto y : g[x]) if(--d[y] == 1) q.push(y);
	}
	if(tt < n){
		for(int i = 1; i <= n; i++) cout << 0 << '\n';
		return 0;
	}
	dfs(rt, 0);
	for(int i = 1; i <= n; i++) cout << res[i] << '\n';
	return 0;
}

posted @ 2025-07-23 16:49  Lordreamland  阅读(9)  评论(0)    收藏  举报