P1494 小Z的袜子 【普通莫队】
我的第二道莫队题,对莫队又有了自己的看法。
在第一题的基础上之上,觉得莫队有个很关键的地方在于 莫队所维护的值是什么,怎么推出维护的公式来。
这道题就是这样,一开始还没自己推出公式来,也有几个坑点。
题目链接:https://www.luogu.org/problemnew/show/P1494
题目大意:给出区间值,询问给定的【l, r】区间内抽到两只颜色一样的概率是多少。
思路:
1.首先很重要的推出公式来,设颜色相同的数量分别为a, b, c...那么对于给定的区间【l, r】,概率为 (a * (a - 1) / 2+ b * (b - 1) / 2 + c * (c - 1) / 2 + ...) / ((r - l + 1) * (r - l) / 2),化简为 (a ^ 2 + b ^ 2 + c ^2 + ... - (r - l + 1)) / ((r - l + 1) * (r - l)),因此cnt维护区间内的值的数量,再推出如何维护平方和的值就行了。
2.计算的数据会爆int,需要用long long ,并且计算的式子要 * 1ll 来转化成long long型,才不会WA。
3.区间 l == r, 分子分母小于等于0时,直接记录 0 / 1
代码如下:
1 #include<stdio.h> 2 #include<math.h> 3 #include<algorithm> 4 using namespace std; 5 const int MAXN = 50010; 6 7 int arr[MAXN]; 8 int cnt[MAXN]; //区间内出现次数 9 10 struct Qurey 11 { 12 int l, r, id; 13 int pos; //所属的块 14 }q[MAXN]; 15 16 struct ANS 17 { 18 long long fz, fm; 19 }ans[MAXN]; 20 21 bool cmp(Qurey a, Qurey b) 22 { 23 if(a.pos == b.pos) 24 return a.r < b.r; 25 else 26 return a.pos < b.pos; 27 } 28 29 long long gcd(long long a, long long b) 30 { 31 long long c; 32 while(b) 33 { 34 c = a % b; 35 a = b; 36 b = c; 37 } 38 return a; 39 } 40 41 int main() 42 { 43 int n, m; 44 scanf("%d%d", &n, &m); 45 for(int i = 1; i <= n; i ++) 46 scanf("%d", &arr[i]); 47 int fk = sqrt(n); 48 for(int i = 1; i <= m; i ++) 49 { 50 scanf("%d%d", &q[i].l, &q[i].r); 51 q[i].id = i; 52 q[i].pos = (q[i].l - 1) / fk + 1; 53 } 54 sort(q + 1, q + 1 + m, cmp); 55 int left = 1, right = 0; 56 long long sum = 0; 57 for(int i = 1; i <= m; i ++) 58 { 59 if(q[i].l == q[i].r) 60 { 61 ans[q[i].id].fz = 0; 62 ans[q[i].id].fm = 1; 63 continue; 64 } 65 while(left > q[i].l) 66 { 67 left --; 68 cnt[arr[left]] ++; 69 sum += 2 *1ll* cnt[arr[left]] - 1; 70 } 71 while(right < q[i].r) 72 { 73 right ++; 74 cnt[arr[right]] ++; 75 sum += 2 *1ll* cnt[arr[right]] - 1; 76 } 77 while(left < q[i].l) 78 { 79 cnt[arr[left]] --; 80 sum -= 2 *1ll* cnt[arr[left]] + 1; 81 left ++; 82 } 83 while(right > q[i].r) 84 { 85 cnt[arr[right]] --; 86 sum -= 2 *1ll* cnt[arr[right]] + 1; 87 right --; 88 } 89 long long fz = sum - (right - left + 1); 90 long long fm = 1ll*(right - left + 1) * (right - left); 91 if(fz <= 0 || fm <= 0) 92 { 93 ans[q[i].id].fz = 0; 94 ans[q[i].id].fm = 1; 95 continue; 96 } 97 long long temp = gcd(fz, fm); 98 fz /= temp, fm /= temp; 99 ans[q[i].id].fz = fz, ans[q[i].id].fm = fm; 100 } 101 for(int i = 1; i <= m; i ++) 102 { 103 printf("%lld/%lld\n", ans[i].fz, ans[i].fm); 104 } 105 return 0; 106 }