理解完题意后,能容易想到答案就是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); }