【题解】[USACO18DEC]The Cow Gathering P
判断 \(x\) 是否能最后一个删,就是以 \(x\) 为根建一颗内向树,然后连边 \(a_i\to b_i\),判断是否有环即可。
所以如果 \(a_i\) 是 \(b_i\) 祖先则无解。所以我们先将所有 \(a_i\) 一侧的子树上的点标记为无解。
关键结论:对于没有标记的节点,要么全部有解,要么全部无解。
首先,没有被标记的点一定连通,因为我们每次标记的是一颗子树。
现在我们假设有一个点有解,那么与之相邻的点一定有解,这两个点记作 \(s, t\)。
假设原先解的序列是 \(A tB s\),那么将 \(t\) 移动到最后得到 \(AB st\) 也是合法序列。两个序列中相对顺序变化的只有 \(t\) 和 \(Bs\),因为 \(t\) 没有被标记,所以不存在限制条件 \(t\) 比某个点先删,所以 \(ABst\) 也是合法序列。
#define N 100005
int n, m, dfn[N], sz[N], mat[N], idx, u[N], v[N], c[N], t, f[N][17], d[N];
vector<int>e[N];
void dfs(int x,int fa){
mat[dfn[x] = ++idx] = x, sz[x] = 1, d[x] = d[f[x][0] = fa] + 1;
rp(i, t)f[x][i] = f[f[x][i - 1]][i - 1];
go(y, e[x])if(y != fa)dfs(y, x), sz[x] += sz[y];
}
int anc(int x,int y){
pre(i, t, 0)if(d[f[x][i]] > d[y])x = f[x][i];
return x;
}
#define ck(x, y) (dfn[x] <= dfn[y] && dfn[x] + sz[x] > dfn[y])
queue<int>q; vector<int>a[N]; int in[N];
void calc(int x,int fa){
go(y, e[x])if(y != fa)calc(y, x), in[x]++, a[y].pb(x);
}
bool check(int x){
rp(i, m)in[v[i]]++, a[u[i]].pb(v[i]);
int tot = 0;
rp(i, n)if(!in[i])q.push(i);
while(!q.empty()){
tot ++; int x = q.front(); q.pop();
go(y, a[x]){
in[y]--;
if(!in[y])q.push(y);
}
}
return tot == n;
}
int main() {
read(n, m), t = log2(n);
rp(i, n - 1){
int x, y;
read(x, y);
e[x].pb(y), e[y].pb(x);
}
dfs(1, 0);
rp(i, m){
int x, y;
read(x, y);
u[i] = x, v[i] = y;
if(ck(x, y)){
y = anc(y, x);
c[1]++, c[dfn[y]]--, c[dfn[y] + sz[y]]++;
}
else c[dfn[x]]++, c[dfn[x] + sz[x]]--;
}
int x = 0;
rp(i, n){
c[i] += c[i - 1];
if(!c[i])x = mat[i];
}
if(check(x)){rp(i, n)printf("%d\n", !c[dfn[i]]);}
else rp(i, n)puts("0");
return 0;
}