传送门:BZOJ 2038

 

题意很明确,是在给定的区间内任意选取两个数,求选到两个相同的数的概率。

 

所以我们得首先统计在给定的区间内,相同的数对有多少对,那么这里就使用到了莫队算法。如果对莫队算法还不够了解,请百度一下,会有比较详细的解释,(虽然有一些也在瞎扯蛋),然后套模版就行了。其次这道题还需要用到一点组合数学知识,即C(n,r)=n!/[r! * (n-r)!]。设每次询问的区间长度为l,那么我们可以很快的计算出从l个数中任取两个的组合数为C(l,2)=l*(l-1)/2,套用公式即可。题目要求的概率也就是:区间内 相同的数对数/任取两个的组合数,再进行约分即可。

 

另外,因为n,m的最大值是50000,所以组合数可能会超过整型范围,所以本题需要开long long

 

代码如下:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <iostream>
  4 #include <algorithm>
  5 #include <cstring>
  6 #include <cmath>
  7 #include <ctime>
  8 #include <queue>
  9 #include <string>
 10 #include <map>
 11 typedef long long ll;
 12 using namespace std;
 13 const int MAXN = 50005;
 14 ll n, m;
 15 ll L, R;
 16 ll block;
 17 ll c[MAXN];
 18 ll cnt[MAXN];
 19 ll ans;
 20 ll s1[MAXN], s2[MAXN];
 21   
 22 struct like {
 23     ll l, r;
 24     ll No;
 25 } qry[MAXN];
 26   
 27 inline ll gi() {
 28     char c;
 29     ll sum = 0, f = 1;
 30     c = getchar();
 31     while (c < '0' || c > '9') {
 32         if (c == '-')
 33             f = -1;
 34         c = getchar();
 35     }
 36     while (c >= '0' && c <= '9') {
 37         sum = sum * 10 + c - '0';
 38         c = getchar();
 39     }
 40     return sum * f;
 41 }
 42   
 43 inline bool cmp(like a, like b) {
 44     if (a.l / block != b.l / block)
 45         return a.l < b.l;
 46     return a.r < b.r;
 47 }
 48   
 49 inline void add(ll p) {
 50     ans += cnt[c[p]];
 51     cnt[c[p]]++;
 52 }
 53   
 54 inline void remove(ll p) {
 55     cnt[c[p]]--;
 56     ans -= cnt[c[p]];
 57 }
 58   
 59 inline void save(ll p, ll a, ll b) {
 60     if (a == 0) {
 61         s1[qry[p].No] = 0;
 62         s2[qry[p].No] = 1;
 63         return;
 64     }
 65     ll tpa = a, tpb = b;
 66     if (tpa < tpb)
 67         swap(tpa, tpb);
 68     while (tpb) {
 69         ll r = tpa % tpb;
 70         tpa = tpb;
 71         tpb = r;
 72     }
 73     s1[qry[p].No] = a / tpa;
 74     s2[qry[p].No] = b / tpa;
 75 }
 76   
 77 int main() {
 78     n = gi();
 79     m = gi();
 80     for (ll i = 1; i <= n; i++)
 81         c[i] = gi();
 82     for (ll i = 1; i <= m; i++) {
 83         qry[i].l = gi();
 84         qry[i].r = gi();
 85         qry[i].No = i;
 86     }
 87     block = sqrt(n);
 88     sort(qry + 1, qry + m + 1, cmp);
 89     L = 1;
 90     R = 1;
 91     cnt[c[1]] = 1;
 92     for (ll i = 1; i <= m; i++) {
 93         while (L < qry[i].l) {
 94             remove(L);
 95             L++;
 96         }
 97         while (L > qry[i].l) {
 98             L--;
 99             add(L);
100         }
101         while (R > qry[i].r) {
102             remove(R);
103             R--;
104         }
105         while (R < qry[i].r) {
106             R++;
107             add(R);
108         }
109         ll l = qry[i].r - qry[i].l + 1;
110         save(i, ans, l * (l - 1) / 2);
111     }
112     for (ll i = 1; i <= m; i++)
113         printf("%lld/%lld\n", s1[i], s2[i]);
114     return 0;
115 }