题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2038
题目大意
给定一个序列。
每次询问一个区间,求在这个区间中选取两个数,两数相同的概率有多大。
算法:
这道题是莫队算法模板题。
莫队算法是一种解决区间询问类问题的通用算法,效率是O(n * sqrt(n)) 乘以每次询问的复杂度。
莫队算法有两种实现方法,一种是欧几里得最小生成树,一种是分块算法。
我采用的是分块算法,编码复杂度比较低。
先把询问按 sqrt(l)排序、分成sqrt(n)个块,然后每块中按照r排序
最后每次从前一个询问修改得到下一个询问的值。
总的时间复杂度是O(n * sqrt(n)) 乘以每次询问的复杂度,在此不再证明。
代码如下:
#include <cstdio> #include <iostream> #include <algorithm> #include <sstream> #include <cstdlib> #include <cstring> #include <string> #include <climits> #include <cmath> #include <queue> #include <vector> #include <stack> #include <set> #include <map> #define INF 0x3f3f3f3f #define eps 1e-8 using namespace std; typedef long long LL; #define mp make_pair #define fi first #define nd second typedef pair <int, int> PII; typedef pair <PII, int> PIII; const int maxn = 51000, maxm = 51000; PIII a[maxm]; long long ans1[maxm], ans2[maxm], cot[maxn]; int c[maxn]; int n, m; LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } void update(int pos, int x, int flg) { ans1[pos] -= cot[c[x]] * cot[c[x]]; cot[c[x]] += flg; ans1[pos] += cot[c[x]] * cot[c[x]]; } bool cmp(const PIII &a, const PIII &b) { int posa = a.fi.fi/(int)sqrt(n); int posb = b.fi.fi/(int)sqrt(n); if(posa == posb) { return a.fi.nd < b.fi.nd; } return posa < posb; } int main() { while(scanf("%d %d", &n, &m) == 2) { memset(cot, 0, sizeof(cot)); for(int i = 1; i <= n; i ++) { scanf("%d", &c[i]); } for(int i = 0; i < m; i ++) { int l, r; scanf("%d %d", &l, &r); a[i] = mp(mp(l, r), i); ans2[i] = r - l + 1; } sort(a, a + m, cmp); for(int i = 0, l = 1, r = 0; i < m; i ++) { if(i) { ans1[a[i].nd] = ans1[a[i - 1].nd]; } else { ans1[i] = 0; } while(l > a[i].fi.fi) { l --; update(a[i].nd, l, 1); } while(l < a[i].fi.fi) { update(a[i].nd, l, -1); l ++; } while(r > a[i].fi.nd) { update(a[i].nd, r, -1); r --; } while(r < a[i].fi.nd) { r ++; update(a[i].nd, r, 1); } } for(int i = 0; i < m; i ++) { ans1[i] -= ans2[i]; ans2[i] *= ans2[i] - 1; if(ans1[i]) { long long k = gcd(ans1[i], ans2[i]); ans1[i] /= k; ans2[i] /= k; } else { ans2[i] = 1; } printf("%lld/%lld\n",ans1[i], ans2[i]); } } return 0; }