选书问题
金牌导航 期望-7
题目大意
有n个人,每个人有自己的选书目录,一个人有p的概率选当前的书,有1-p的概率不选,即去查看下一本书(过n后回到1),现在问你选书的逆序对的期望数
输入样例
5 5
0.5
5 1
3 2
2 2
2 1
3 1
输出样例
0.89
数据范围
1⩽N,M⩽5×105,0.4⩽p⩽0.61\leqslant N,M\leqslant 5\times 10^5,0.4\leqslant p \leqslant 0.61⩽N,M⩽5×105,0.4⩽p⩽0.6
解题思路
对于1个人,设f_i为选第i本书的期望值,num_i为选书目录里里书的数量
那么有:
f2=f1p×(1−p)×p=f1×(1−p)f_2=\frac{f_1}{p}\times (1-p)\times p = f_1\times (1-p)f2=pf1×(1−p)×p=f1×(1−p)
f1p\frac{f_1}{p}pf1为查看f1f_1f1的期望值,1-p是第一本书不选,p为选第二本书
同理,则有:
f3=f2p×(1−p)×p=f2×(1−p)=f1×(1−p)2f4=f3p×(1−p)×p=f3×(1−p)=f1×(1−p)3...fnumi=fnumi−1p×(1−p)×p=fnumi−1×(1−p)=f1×(1−p)numi−1f_3=\frac{f_2}{p}\times (1-p)\times p = f_2\times (1-p)= f_1\times (1-p)^2\\ f_4=\frac{f_3}{p}\times (1-p)\times p = f_3\times (1-p)= f_1\times (1-p)^3\\ ...\\ f_{num_i}=\frac{f_{num_i - 1}}{p}\times (1-p)\times p = f_{num_i-1}\times (1-p)= f_1\times (1-p)^{num_i-1}f3=pf2×(1−p)×p=f2×(1−p)=f1×(1−p)2f4=pf3×(1−p)×p=f3×(1−p)=f1×(1−p)3...fnumi=pfnumi−1×(1−p)×p=fnumi−1×(1−p)=f1×(1−p)numi−1
那么有(+p为最初始的一次):
f1=fnumip×(1−p)×p=fnumi×(1−p)=f1×(1−p)numi+pf_1=\frac{f_{num_i}}{p}\times (1-p)\times p = f_{num_i}\times (1-p)= f_1\times (1-p)^{num_i}+pf1=pfnumi×(1−p)×p=fnumi×(1−p)=f1×(1−p)numi+p
解该方程:
f1=f1×(1−p)numi+pf1×(1−(1−p)numi)=pf1=p1−(1−p)numi\begin{aligned}f_1 & = f_1\times (1-p)^{num_i}+p \\ f_1\times(1-(1-p)^{num_i}) & =p\\f_1&=\frac{p}{1-(1-p)^{num_i}}\end{aligned}f1f1×(1−(1−p)numi)f1=f1×(1−p)numi+p=p=1−(1−p)numip
得到f后,利用树状数组求逆序对即可
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 500010
using namespace std;
int n, m, w, s[N], num[N], first[N], last[N], next[N];
double p, g, ans, c[N], pw[N], sum;
struct node
{int x, y;
}a[N];
double ask(int x)//树状数组
{double sum = 0;for (; x; x -= x&-x)sum += c[x];return sum;
}
void add(int x, double y)
{for (; x <= 500000; x += x&-x)c[x] += y;return;
}
bool cmp(node x, node y)
{return x.x < y.x || x.x == y.x && x.y < y.y;//按编号排序
}
int main()
{scanf("%d%d%lf", &n, &m, &p);for (int i = 1; i <= m; ++i){scanf("%d%d", &a[i].x, &a[i].y);num[a[i].x]++;}sort(a + 1, a + 1 + m, cmp);pw[0] = 1;for (int i = 1; i <= n; ++i)pw[i] = pw[i - 1] * (1 - p);for (int i = 1; i <= m; ++i){if (a[i].x != a[i - 1].x) g = p / (1.0 - pw[num[a[i].x]]);//新的一个数的期望else g = g * (1 - p);//不是新的就乘1-p来求ans += (sum - ask(a[i].y)) * g;//前面加入的人的编号都比当前小,只要找到书的编号大的即可add(a[i].y, g);sum += g;}printf("%.2lf", ans);return 0;
}