1. 程式人生 > >BZOJ4481: [Jsoi2015]非誠勿擾【概率期望+樹狀陣列】

BZOJ4481: [Jsoi2015]非誠勿擾【概率期望+樹狀陣列】

Description

【故事背景】
JYY趕上了網際網路創業的大潮,為非常勿擾開發了最新的手機App實現單身
大齡青年之間的“速配”。然而隨著使用者數量的增長,JYY發現現有速配的演算法似
乎很難滿足大家的要求,因此JYY決定請你來調查一下其中的原因。
【問題描述】
應用的後臺一共有N個女性和M個男性,他們每個人都希望能夠找到自己的
合適伴侶。為了方便,每個男性都被編上了1到N之間的一個號碼,並且任意兩
個人的號碼不一樣。每個女性也被如此編號。
JYY應用的最大特點是賦予女性較高的選擇權,讓每個女性指定自己的“如
意郎君列表”。每個女性的如意郎君列表都是所有男性的一個子集,並且可能為
空。如果列表非空,她們會在其中選擇一個男性作為自己最終接受的物件。
JYY用如下演算法來為每個女性速配最終接受的男性:將“如意郎君列表”中的
男性按照編號從小到大的順序呈現給她。對於每次呈現,她將獨立地以P的概率
接受這個男性(換言之,會以1−P的概率拒絕這個男性)。如果她選擇了拒絕,
App就會呈現列表中下一個男性,以此類推。如果列表中所有的男性都已經呈現,
那麼中介所會重新按照列表的順序來呈現這些男性,直到她接受了某個男性為止。
顯然,在這種規則下,每個女性只能選擇接受一個男性,而一個男性可能被多個
女性所接受。當然,也可能有部分男性不被任何一個女性接受。
這樣,每個女性就有了自己接受的男性(“如意郎君列表”為空的除外)。現
在考慮任意兩個不同的、如意郎君列表非空的女性a和b,如果a的編號比b的編
號小,而a選擇的男性的編號比b選擇的編號大,那麼女性a和女性b就叫做一對
不穩定因素。
由於每個女性選擇的男性是有一定的隨機性的,所以不穩定因素的數目也是
有一定隨機性的。JYY希望你能夠求得不穩定因素的期望個數(即平均數目),
從而進一步研究為什麼速配演算法不能滿足大家的需求。

Input

輸入第一行包含2個自然數N,M,表示有N個女性和N個男性,以及所有女
性的“如意郎君列表”長度之和是M。
接下來一行一個實數P,為女性接受男性的概率。
接下來M行,每行包含兩個整數a,b,表示男性b在女性a的“如意郎君列表”
中。
輸入保證每個女性的“如意郎君列表”中的男性出現切僅出現一次。
1≤N,M≤500,000,0.4≤P<0.6

Output

輸出1行,包含一個實數,四捨五入後保留到小數點後2位,表示不穩定因素的期望數目。

Sample Input

5 5
0.5
5 1
3 2
2 2
2 1
3 1

Sample Output

0.89


思路

直接考慮每個男人的期望貢獻就可以了

用樹狀陣列來維護

直接把每個男人的被選概率算出來統計答案

這東西推一下等比數列求和就可以了

水題

不過要卡精度,勇士請使用long double


#include<bits/stdc++.h>

using namespace std;

typedef long double lb;
const int N = 1e6 + 10;

int n, m;
lb bit[N], p;
vector<int> g[N];

lb fast_pow(lb a, int b) {
  lb res = 1.0;
  while (b) {
    if (b & 1) res *= a;
    b >>= 1;
    a *= a; 
  } 
  return res;
}

void add(int x, lb val) {
  while (x < N) {
    bit[x] += val;
    x += x & (-x);
  }
}

lb query(int x) {
  lb res = 0;
  while (x) {
    res += bit[x];
    x -= x & (-x);
  }
  return res;
}

lb query(int l, int r) {
  return query(r) - query(l - 1);
}

int main() {
  scanf("%d %d", &n, &m);
  scanf("%Lf", &p);
  for (int i = 1; i <= m; i++) {
    int u, v; scanf("%d %d", &u, &v);
    g[u].push_back(v);
  }
  for (int i = 1; i <= n; i++) {
    sort(g[i].begin(), g[i].end());
  }
  lb ans = 0.0;
  for (int i = 1; i <= n; i++) {
    int len = g[i].size();
    for (int j = 0; j < len; j++) {
      lb cur = p * fast_pow(1.0 - p, j) / (1.0 - fast_pow(1.0 - p, len));
      ans += query(g[i][j] + 1, m) * cur;
      add(g[i][j], cur);
    }
  }
  printf("%.2Lf", ans);
  return 0;
}