【YBTOJ】【Luogu P6089】[JSOI2015]非诚勿扰

链接:

题目

博客园

题目大意:

\(n\) 个甲类点和 \(n\) 个乙类点。每个甲点可以和若干个乙点连接,对于每一个乙点,它会以 \(p\) 的概率选择此点,不选择就跳到下一个点,若最后一点不选择,则跳到第一点。求出期望逆序对个数。

正文:

假设一个甲点可以和 \(k\) 个乙点连接。

因为像一个环一样,可以转几圈。第一个乙点在第一圈被选中的概率是 \(p\),第二圈被选中的概率就是 \((1-p)^kp\)(即不选它 \(k\) 次也就转了一圈,转完后再选),第三圈被选中的概率是 \((1-p)^{2k}p\),以此类推第一个乙点被选中的概率是:

\[E_1=\sum_{i=0}^{\infty}(1-p)^{ik}p \]

通过等比数列求和公式得到:

\[E_1=\frac{1-(1-p)^{\infty}}{1-(1-p)^{k}p}p=\frac{p}{1-(1-p)^{k}p} \]

式子中 \((1-p)^{\infty}\) 无限大所以 \(1-(1-p)^{\infty}\) 无限小,所以可以把它化掉。


那么 \(E_1\) 求好了,剩下的呢?很容易得到 \(E_i=E_{i-1}\cdot(1-p)\),因为 \(i\) 相当于是先拒绝了 \(i-1\) 后得来的。

剩下的就和题目大意的一样,求一个逆序对就搞定了。

代码:

int n, m;
long double p, t[N], ans;

vector <int> v[N];

void add (int x, long double k) {for (; x < n; x += x & -x) t[x] += k; }
long double query (int x) {long double ans = 0;for (; x; x -= x & -x) ans += t[x]; return ans;}

long double qpow(long double a, int b)
{
	long double ans = 1;
	for (; b; b >>= 1) {if(b & 1)ans *= a; a *= a;} 
	return ans;
}

int main()
{
	scanf ("%d%d", &n, &m);
	scanf ("%Lf", &p);
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf ("%d%d", &x, &y);
		v[x].push_back(y);
	}
	for (int i = 1; i <= n; i++) sort (v[i].begin(), v[i].end());
	for (int i = 1; i <= n; i++)
	{
		if (v[i].empty()) continue;
		int k = v[i].size();
		long double P = p / (1 - qpow(1 - p, k));
		for (int j = 0; j < k; j++)
			ans += P * query(n - v[i][j]),
			P *= 1 - p;
		P = p / (1 - qpow(1 - p, k));
		for (int j = 0; j < k; j++)
			add(n + 1 - v[i][j], P),
			P *= 1 - p;
	}
	printf ("%.2Lf", ans);
	return 0;
}
posted @ 2021-01-11 20:33  Jayun  阅读(92)  评论(0编辑  收藏  举报