解析
我们逐级考虑这个问题
没有粉色边的情况
先维护一个列表 \text{top},里面的每个结点都是一颗仅由绿色边组成的树的根,且根可以到达树中的每个结点
再记录一个列表 \text{delete},里面的每个结点都是可以被 \text{top} 列表中的某个结点到达的
一开始,显然所有点都应该在 \text{top} 里。每次我们从 \text{top} 中取任意两个点 u, v 询问绿色边的方向。如果边是 (u, v),那么就把 v 放入 \text{delete},u 继续留在 \text{top};反之亦然。如此直到 \text{top} 中只剩下一个结点,显然这个结点就是答案;且询问次数是严格 n-1 次的
(值得一提的是,这种情况下该算法给出的是最优(询问最少)的方案)
粉色边组成 DAG 的情况
同样维护列表 \text{top} 并记录列表 \text{delete}。但我们一开始不能将所有结点都放入 \text{top} 了,否则我们询问的两个结点间可能会已经有粉色边了
考虑这样一种做法:初始仅将 DAG 中入度为 0 的结点放入 \text{top};每次将结点放入 \text{delete} 时,就在 DAG 中 “删去” 这个结点,并检测是否有新的入度为 0 的结点,有就将其放入 \text{top}
如此直到 \text{top} 中只剩下一个结点时,其余的结点要么在 \text{delete} 里,那么它们可以被 \text{top} 中的结点通过绿色路径到达;要么就在 DAG 未被删除的部分中,可以发现这些结点一定能被 \text{top} 中剩下的那个结点通过粉色路径到达(证明考虑 DAG 的拓扑排序性质;正是 \text{top} 中剩下的那个结点连出的边才使这些结点不被删除)。于是 \text{top} 中剩下的这个结点就是答案;且在最多 n-1 次询问后我们就能给出答案
(这种情况下该算法给出的不一定是最优的方案)
任意的情况
这里的做法可能会很多(主要是在一些细节上的差异),但大体都是强联通分量(SCC)缩 DAG 然后再处理。这里就只讲下我实现的做法
一开始我们将图中所有的 SCC 求出,并把每个 SCC 导出子图中所有的边(也就是这些 SCC “内部” 的边)都删掉,这样原图就转化为了 DAG。我们仍按照 DAG 的方式做,但对于每个 SCC,我们至多允许其中的一个结点出现在 \text{top} 列表中(为了做到这点,我们可以考虑对每个 SCC 维护一个列表 scc_list
,其中是该 SCC 中入度已经为 0,但因为限制无法放入 \text{top} 的结点)。这样我们就保证了每次询问的两个结点间不会有粉色边
当 \text{top} 中只剩下一个结点时,设其为 u,其余的结点要么在 \text{delete} 里,那么它们可以被 u 通过绿色路径到达;要么就在 DAG 未被删除的部分中,或为 u 强联通分量中的结点(注意 u 强联通分量中的部分结点也可能在 \text{delete} 中,不过其实并不需要考虑这个问题),前者一定可以被 u 的强联通分量通过粉色路径到达,而后者就在 u 的强联通分量中,显然也可被 u 通过粉色路径到达
这样,我们在至多 n-1 次询问后就能给出答案。注意得出的方案并不一定是最优的,不过题目并没有要求方案的最优性
CODE
另外注意询问要换行,且要**先刷新屏幕**再读取回答 \fad
```cpp
#include <cstdio>
#include <cstring>
#include <vector>
using std::vector;
using std::min;
const int MAXN =1e5+20;
/*------------------------------Map------------------------------*/
int first[MAXN], tote;
struct edge{
int to, net;
}e[MAXN];
inline void clear_map(){
tote =0;
memset(first, 0, sizeof(first));
}
inline void addedge(const int &u, const int &v){
++tote;
e[tote].to =v, e[tote].net =first[u];
first[u] =tote;
}
/*------------------------------Scc------------------------------*/
vector<int> scc_list[MAXN];
int scc_id[MAXN], in[MAXN];
/*------------------------------Tarjan------------------------------*/
int dfn[MAXN], low[MAXN], cnt;
int stk[MAXN], stk_top;
bool instk[MAXN];
void tarjan(int u){
low[u] =dfn[u] =++cnt;
stk[stk_top++] =u;
instk[u] =1;
for(int l =first[u]; l; l =e[l].net){
if(!dfn[e[l].to]){
tarjan(e[l].to);
low[u] =min(low[u], low[e[l].to]);
}
else if(instk[e[l].to])
low[u] =min(low[u], dfn[e[l].to]);
}
if(low[u] == dfn[u])
while(stk[stk_top] != u){
scc_id[stk[stk_top-1]] =u;/*姑且就以 u 作为 SCC 颜色*/
instk[stk[stk_top-1]] =0;
--stk_top;
}
}
int tmp_e_u[MAXN], tmp_e_v[MAXN], tmp_e_tot;/*临时存边*/
void rebuild_DAG(int n){
for(int i =1; i <= n; ++i)
for(int l =first[i]; l; l =e[l].net)
if(scc_id[e[l].to] != scc_id[i]){
tmp_e_u[tmp_e_tot] =i;
tmp_e_v[tmp_e_tot] =e[l].to;
++tmp_e_tot;
}
clear_map();
for(int i =0; i < tmp_e_tot; ++i){
addedge(tmp_e_u[i], tmp_e_v[i]);
++in[tmp_e_v[i]];
}
}
/*------------------------------Main------------------------------*/
inline int read(){
int x =0; char c =getchar();
while(c < '0' || c > '9') c =getchar();
while(c >= '0' && c <= '9') x = (x<<3) + (x<<1) + (48^c), c =getchar();
return x;
}
int top[MAXN], head, tail;
bool in_top[MAXN];
int main(){
int n =read(), m =read();
for(int i =0; i < m; ++i){
int u =read(), v =read();
addedge(u, v);
}
for(int i =1; i <= n; ++i)
if(!dfn[i])
tarjan(i);
rebuild_DAG(n);
for(int i =1; i <= n; ++i)
if(in[i] == 0){
if(!in_top[scc_id[i]]){
top[tail++] =i;
in_top[scc_id[i]] =1;
}
else
scc_list[scc_id[i]].push_back(i);
}
while(tail-head > 1){
int u =top[head], v =top[head+1];
head +=2;
printf("? %d %d\n", u, v);
fflush(stdout);
if(read() == 0)
u ^=v ^=u ^=v;
top[--head] =u;/*将 u 放回,从 head 加入没有特殊含义*/
in_top[scc_id[v]] =0;
for(int l =first[v]; l; l =e[l].net){
--in[e[l].to];
if(in[e[l].to] == 0){
if(!in_top[scc_id[e[l].to]]){
top[tail++] =e[l].to;
in_top[scc_id[e[l].to]] =1;
}
else
scc_list[scc_id[e[l].to]].push_back(e[l].to);
}
}
if((int)scc_list[scc_id[v]].size() > 0){
top[tail++] =scc_list[scc_id[v]][scc_list[scc_id[v]].size()-1];
scc_list[scc_id[v]].pop_back();
in_top[scc_id[v]] =1;
}
}
printf("! %d", top[head]);
}
```