题目链接: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;
}

591

被折叠的 条评论
为什么被折叠?



