[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;
}
$\color{blue} \mathcal {Lordreamland}$

浙公网安备 33010602011771号