CF1142E Pink Floyd强连通分量,构造

Posted athousandmoons

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1142E Pink Floyd强连通分量,构造相关的知识,希望对你有一定的参考价值。

又是一道交互题

题目描述:你有一个 (n) 个点的竞赛图,有 (m) 条边为粉红色,其余为绿色。粉红色边的方向已知,绿色边方向未知,但你可以询问不超过 (2n) 次一条边 ({u,v}),交互器会告诉你这条边的方向。求一个点 (u),使得对于任意 (v eq u)(u) 可以只经过一种颜色的边到达 (v)

数据范围:(n,mle 10^5)


解法实在有点绕...

首先如果 (m=0),我们维护一个答案集合 (S) 表示目前可能的答案。一开始 (S=V),之后要一步步排除。若 ((u,v)in E),则 (v) 是答案 (Rightarrow u) 是答案。所以可以不考虑 (v),把 (v)(S) 中删掉。最后只剩下一个点就是答案。

但是如果有粉红色边的话,有一些边就不能用了,但是答案点可以通过粉红色边与其他点联通。所以先把粉红色边缩点,每个强连通分量中只取出一个点加入初始的 (S),然后按照上面的方法做。

但是如果 ((u,v)in E) 则不能直接将 (v) 删掉,因为 (v) 所在的强连通分量由粉红色边组成,不能和绿色边在一条路径中。

于是你可以把每个强连通分量中的一些边删掉使得它成为一个 DAG,每次把入度为 (0) 的点加入,询问得到 ((u,v)in E) 时把 (v) 删掉,再看剩下的点中有没有入度为 (0) 的点加入。剩下的最后一个点就是答案。

正确性证明:被删过的点可以通过绿边到达,没有被删过的点可以通过粉红色边到达。于是 (n-1) 次询问就可以直接搞定?

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define PB push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 100003, mod = 998244353;
inline int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < ‘0‘ || ch > ‘9‘;ch = getchar()) f |= ch == ‘-‘;
	for(;ch >= ‘0‘ && ch <= ‘9‘;ch = getchar()) x = x * 10 + ch - ‘0‘;
	if(f) x = -x;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, m, cnt, head[N], to[N], nxt[N], deg[N];
bool ins[N], vis[N];
vector<int> E[N], now;
inline void add(int a, int b){to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;}
void dfs(int x){
	ins[x] = vis[x] = true;
	for(Rint i = head[x];i;i = nxt[i]){
		if(!ins[to[i]]){E[x].PB(to[i]); ++ deg[to[i]];}
		if(!vis[to[i]]) dfs(to[i]);
	}
	ins[x] = false;
}
int main(){
	read(n); read(m);
	for(Rint i = 1, a, b;i <= m;++ i){
		read(a); read(b); add(a, b);
	}
	for(Rint i = 1;i <= n;++ i) if(!vis[i]) dfs(i);
	for(Rint i = 1;i <= n;++ i) if(!deg[i]) now.PB(i);
	while(now.size() > 1){
		int u = now.back(); now.pop_back();
		int v = now.back(), f; now.pop_back();
		printf("? %d %d
", u, v); fflush(stdout);
		scanf("%d", &f); if(f) swap(u, v); now.push_back(v);
		for(Rint w : E[u]) if(!-- deg[w]) now.PB(w); E[u].clear();
	}
	printf("! %d
", now.front()); fflush(stdout);
}

以上是关于CF1142E Pink Floyd强连通分量,构造的主要内容,如果未能解决你的问题,请参考以下文章

CF949C Data Center Maintenance Tarjan找强连通分量

CF711DDirected Roads(想法题,环,强连通分量)

CodeForces 732F Tourist Reform

tarjan求强连通+缩点——cf1248E

强连通分量

强连通分量(tarjan求强连通分量)