CodeForces 1142E Pink Floyd

洛谷传送门

CF 传送门

感觉很神奇啊,想了挺久的。

如果没有粉色边是容易的。竞赛图中,强连通分量缩点后,拓扑序较小的点向每一个拓扑序比它大的点连边。

利用这个性质,我们维护一个答案集合 \(S\),当 \(|S| \ge 2\) 时,每次随意取出两个点 \(u, v \in S\)。如果边的方向是 \(u \to v\) 那么删掉 \(v\),否则删掉 \(u\)

正确性显然。如果 \(u, v\) 不在同一个强连通分量,那我们保留了拓扑序较小的强连通分量中的点;如果在,我们随机保留一个就好了。直到 \(|S| = 1\) 时,剩下的唯一点就是在拓扑序最小的强连通分量中。

有粉色边的情况,我们先把粉色边构成的强连通分量缩起来,从入度为 \(0\) 的分量中随便取一个点组成初始的 \(S\)。仍然是做一遍上面的流程,只不过删除一个点时把它入度为 \(0\) 的后继加入 \(S\)

这样的话,如果我们最后选的点是通过前驱被删掉而加入 \(S\) 的,那它一定能通过绿色边走到它的前驱。它的后继通过粉色边也能走到。如果最后选的点原来没有粉色边相连,由于我们询问的方式,它能只走绿色边到达全部点(感性理解)。

具体实现时可以不需要 Tarjan,类似拓扑排序删除一些构成环的边即可。

由于每次询问都会从 \(S\) 中删除一个点,所以询问次数 \(\le n - 1\)

code
// Problem: E. Pink Floyd
// Contest: Codeforces - Codeforces Round 549 (Div. 1)
// URL: https://codeforces.com/problemset/problem/1142/E
// Memory Limit: 256 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 100100;

int n, m, ind[maxn];
bool vis[maxn], siv[maxn];
vector<int> G[maxn], T[maxn];

void dfs(int u) {
	vis[u] = siv[u] = 1;
	for (int v : T[u]) {
		if (!siv[v]) {
			G[u].pb(v);
			++ind[v];
		}
		if (!vis[v]) {
			dfs(v);
		}
	}
	siv[u] = 0;
}

inline int ask(int x, int y) {
	printf("? %d %d\n", x, y);
	fflush(stdout);
	scanf("%d", &x);
	return x;
}

void solve() {
	scanf("%d%d", &n, &m);
	while (m--) {
		int u, v;
		scanf("%d%d", &u, &v);
		T[u].pb(v);
	}
	for (int i = 1; i <= n; ++i) {
		if (!vis[i]) {
			dfs(i);
		}
	}
	vector<int> S;
	for (int i = 1; i <= n; ++i) {
		if (!ind[i]) {
			S.pb(i);
		}
	}
	while ((int)S.size() > 1) {
		int u = *(--S.end()), v = *(----S.end());
		if (ask(u, v)) {
			S.erase(----S.end());
			for (int w : G[v]) {
				if (!(--ind[w])) {
					S.pb(w);
				}
			}
		} else {
			S.erase(--S.end());
			for (int w : G[u]) {
				if (!(--ind[w])) {
					S.pb(w);
				}
			}
		}
	}
	printf("! %d\n", S[0]);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2023-07-06 20:29  zltzlt  阅读(17)  评论(0编辑  收藏  举报