我们先考虑只有绿边的情况。那么显然,只要一个点在 \(n-1\) 次讯问中与它有关的边都为出边,那么这个点一定是一个合法的答案。
现在出现了粉边。我们先将点缩成强连通分量,再进行上述操作。由于不能在一条路径中同时出现粉边和绿边,所以我们在确认一个点不可行之后,要将在遍历到它时的所有横插出边和树出边全部塞进去。
时间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,dfn[N],vis[N],rd[N],tp;
vector<int>g[N],ve[N];int st[N];
void tarjan(int x){dfn[x]=vis[x]=1;for(auto y:g[x]){if(!vis[y])ve[x].push_back(y),rd[y]++;if(!dfn[y]) tarjan(y);}vis[x]=0;
}int que(int x,int y){cout<<"? "<<x<<" "<<y<<endl;int fl;return cin>>fl,fl;
}int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1,x,y;i<=m;i++)cin>>x>>y,g[x].push_back(y);for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);for(int i=1;i<=n;i++) if(!rd[i]) st[++tp]=i;while(tp>1){int u=st[tp--];if(que(u,st[tp])==1) swap(u,st[tp]);for(auto v:ve[u]) (rd[v]<2)?st[++tp]=v:rd[v]--;}cout<<"! "<<st[1]<<endl;return 0;
}