链接:
题目大意:
有 (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;
}