码迷,mamicode.com
首页 > 其他好文 > 详细

CF1142E Pink Floyd【强连通分量,构造】

时间:2020-06-16 20:07:33      阅读:51      评论:0      收藏:0      [点我收藏+]

标签:ret   getch   mat   inline   超过   can   f11   stdout   pre   

又是一道交互题

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

数据范围:\(n,m\le 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\n", 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\n", now.front()); fflush(stdout);
}

CF1142E Pink Floyd【强连通分量,构造】

标签:ret   getch   mat   inline   超过   can   f11   stdout   pre   

原文地址:https://www.cnblogs.com/AThousandMoons/p/13144017.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!