CF1142E Pink Floyd
Solution
首先考虑没有粉色边的时候怎么做,我们设 \(S\) 是可能成为答案的集合,可以看出我们可以先把 \(n\) 个点都加进去,每次随便选 \((u,v)\),假设是 \(u\to v\),那么我们就把 \(v\) 踢掉,可以看出最后一个点就是答案。
考虑一开始有粉色边怎么做。我们考虑先把强连通分量缩成一个点。然后从每个强连通分量中选一个点进行上面的步骤。但是我们可以发现一个问题是我们踢掉 \(v\) 之后,\(u\to v\) 是粉色的,但是 \(v\) 到它的强连通分量是绿色的,所以我们就需要把 \(v\) 所在的强连通分量中的点也加进去考虑。所以我们可以对每个强连通分量保留一个 DAG,每次删掉 \(v\) 就把新的入度为 \(0\) 加入 \(S\) 就好了。
注意到最后的根到曾经加入 \(S\) 的点都可以用绿色边形成一个树结构,未加入 \(S\) 的点就是根所属的强连通分量,显然可以用粉色边走到。查询次数是 \(\le n-1\) 次的。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 100005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m,deg[MAXN];
vector <int> g[MAXN],t[MAXN];
bool vis[MAXN],ins[MAXN];
void dfs (int u){
vis[u] = ins[u] = 1;
for (Int v : g[u]){
if (!ins[v]) t[u].push_back (v),deg[v] ++;
if (!vis[v]) dfs (v);
}
ins[u] = 0;
}
bool query (int u,int v){
printf ("? %d %d\n",u,v),fflush (stdout);
int x;scanf ("%d",&x);
return x;
}
signed main(){
read (n,m);
for (Int i = 1,u,v;i <= m;++ i) read (u,v),g[u].push_back (v);
for (Int u = 1;u <= n;++ u) if (!vis[u]) dfs (u);
vector <int> S;
for (Int u = 1;u <= n;++ u) if (!deg[u]) S.push_back (u);
while (S.size() > 1){
int u = S.back();S.pop_back ();int v = S.back();S.pop_back();
if (!query (u,v)) swap (u,v);S.push_back (u);
for (Int x : t[v]) if (!-- deg[x]) S.push_back (x);
}
printf ("! %d\n",S.front());
return 0;
}