HYSBZ 2438杀人游戏(tarjan 缩点 + 思维)好题!!!

理解完题意后,能容易想到答案就是1 - 未知身份人数 / 总人数。所以我们就把问题转换成求一共有几个人是身份是未知的(即他的身份是无法从他人那里获取的)。因为其他人我们可以通过这些人来安全的推导出他的身份。所谓的递推吧...

于是我们把n个人看成图上的n个点,认识关系看成点与点之间的有向边。容易发现未知身份的人数就是图中入度为0的点的个数。但是又考虑到可能会出现环,所以我们需要用 tarjan 缩点,即把一个环(强连通分量)缩成一个点,因为一个强连通分量内,我们只要知道一个人的身份我们就可以安全的去确定其他人的身份,所以一个环就等价与一个点。

所以未知身份人数就是用tarjan对原图缩完点后,入度为0的点的个数。但是这还不够,我们需要特判一种特殊情况,比如A认识C,B也认识C,按照我们刚刚讲的话,未知人数是2,A和B,但是事实上我只要问A的身份就够了。因为通过问A我可以得知C的身份,此时只剩下了B一个人,那么通过排除法B的身份是不是就确定了,我都不用去问他了。

所以我们缩完点后需要特判一下是否存在一个点,这个点的大小必须为1,并且他所有的出边通向的点都有其他的点可以到达(即入度都大于1的)。那么未知身份的人数就减一。如果有多个这样的点也还是减一。具体为什么留给读者直接思考了吧...

(最近菜鸡刚学完 tarjan,不禁再次感叹 tarjan缩点的算法真的是太妙了 awsl!

上代码

//#include<bits/stdc++.h>
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ll, int> pli;
typedef pair<int, ll> pil;
#define X first
#define Y second
#define pb push_back
inline ll gcd(ll a, ll b) { while (b != 0) { ll c = a % b; a = b; b = c; }return a < 0 ? -a : a; }
inline ll lcm(ll a, ll b) { return (a * b) / gcd(a, b); }
inline ll lowbit(ll x) { return x & (-x); }
const double PI = 3.14159265358979323846;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
inline char nc() {
	static char buf[1 << 21], * p1 = buf, * p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
inline ll rd() {
	//#define getchar nc
	ll x = 0, f = 1; char ch = getchar();
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); }
	return x * f;
}
const double eps = 1e-8;
const int M = 3e5 + 10;
const int N = 1e5 + 10;
struct Edge {
	int to, next;
}e[M];
int head[N];
int tot, num, cnt, top;
int dfn[N], low[N], stk[N], c[N], scc[N], ins[N];
int n, m;
void add(int x, int y) {
	e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
}
void tarjan(int x) {
	dfn[x] = low[x] = ++num;
	stk[++top] = x, ins[x] = 1;
	for (int i = head[x]; i != -1; i = e[i].next) {
		int y = e[i].to;
		if (!dfn[y]) {
			tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if (ins[y])
			low[x] = min(low[x], dfn[y]);
	}
	if (dfn[x] == low[x]) {
		cnt++;
		int tmp;
		do {
			tmp = stk[top--], ins[tmp] = 0;
			c[tmp] = cnt, scc[cnt]++;
		} while (x != tmp);
	}
}
vector<int>ver[N];
int deg[N];
bool check(int x) {
	for (auto& y : ver[x]) {
		if (deg[y] == 1)return 0;
	}
	return 1;
}
void init() {
	cnt = num = top = tot = 0;
	memset(head, -1, sizeof head);
}
int main() {
	n = rd(), m = rd();
	init();
	while (m--) {
		int u = rd(), v = rd();
		add(u, v);
	}
	for (int i = 1; i <= n; i++) {
		if (!dfn[i])tarjan(i);
	}
	for (int x = 1; x <= n; x++) {
		for (int i = head[x]; i != -1; i = e[i].next) {
			int y = e[i].to;
			if (c[x] == c[y])continue;
			ver[c[x]].pb(c[y]);
			deg[c[y]]++;
		}
	}
	int kk = 0;
	bool flag = 0;
	for (int i = 1; i <= cnt; i++) {
		if (!deg[i]) {
			kk++;
			if (!flag) {
				if (scc[i] == 1 && check(i))flag = 1;
			}
		}
	}
	if (flag)kk--;
	printf("%.6f\n", (1.0 * n - kk) / n);
}

  

猜你喜欢

转载自www.cnblogs.com/fengfeng007/p/12293486.html