Loading

【题解】[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;
}
posted @ 2022-02-13 10:24  7KByte  阅读(72)  评论(0编辑  收藏  举报