[Description]
【问题描述】
作为一个生活散漫的人,小
Z
每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小
Z
再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小
Z
把这
N
只袜子从
1
到
N
编号,然后从编号
L
到
R (L<R)
的这
R-L+1
只袜子中随机抽出两只穿上。
尽管小
Z
并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小
Z
,他有多大的概率抽到两只颜色相同的袜子。当然,小
Z
希望这个概率尽量高,所以他可能会询问多个
(L,R)
以方便自己选择。
【输入文件】
输入文件名为
hose.in
。
输入文件第一行包含两个正整数
N
和
M
。
N
为袜子的数量,
M
为小
Z
所提的询问的数量。
接下来一行包含
N
个正整数
Ci
,其中
Ci
表示第
i
只袜子的颜色,相同的颜色用相同的数字表示。
再接下来
M
行,每行两个正整数
L
,
R
表示一个询问。
【输出文件】
输出文件名为
hose.out
。
输出文件包含
M
行,对于每个询问在一行中输出分数
A/B
表示从该询问的区间
[L,R]
中随机抽出两只袜子颜色相同的概率。若该概率为
0
则输出
0/1
,否则输出的
A/B
必须为最简分数。(详见样例)
【输入样例】
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
【输出样例】
2/5
0/1
1/1
4/15
【样例解释】
询问
1
:共
C(5,2)=10
种可能,其中抽出两个
2
有
1
种可能,抽出两个
3
有
3
种可能,概率为
(1+3)/10=4/10=2/5
。
询问
2
:共
C(3,2)=3
种可能,无法抽到颜色相同的袜子,概率为
0/3=0/1
。
询问
3
:共
C(3,2)=3
种可能,均为抽出两个
3
,概率为
3/3=1/1
。
注:上述
C(a, b)
表示组合数,组合数
C(a, b)
等价于在
a
个不同的物品中选取
b
个的选取方案数。
【数据规模和约定】
30%
的数据中
N,M ≤ 5000
;
60%
的数据中
N,M ≤ 25000
;
100%
的数据中
N,M ≤ 50000
,
1 ≤ L < R ≤ N
,
Ci ≤ N
。
[Algorithm & Data Structure]
莫队算法 分块
[Analysis]
莫队算法支持解决区间查询无修改的问题。要求满足[l, r]答案推到 [l, r + 1]或[l + 1, r]需要时间为O(1)
(整个算法时间复杂度为O(nsqrt(n))), 或[l, r]到[l, r + 1]为O(log(n))
(整个算法时间复杂度为O(nsqrt(n)log(n))
莫队的思想是将询问转化为坐标系中的点,然后找出它们的曼哈顿最小生成树,按照曼哈顿最小生成树的dfs遍历顺序做,就能取得最小的复杂度。当然普通的曼哈顿最小生成树算法比较复杂(分成八块的那种),我们可以采用分块的做法。将整个序列分成sqrt(n)块,将所有的query按照左端点所在区间为第一关键字、右端点为第二关键字进行排序,按照这个顺序进行查询,时间复杂度O(nsqrt(n))
why?首先看块内,右端点的变化最多为n,一共有sqrt(n)块,所以复杂度nsqrt(n)。然后左端点每一次的变化不会超过sqrt(n),一共有n次变化,所以nsqrt(n)。最后每到新的一块重新开始查询,nsqrt(n)。 加起来总的复杂度是O(nsqrt(n))
[Code]
/**************************************************************
Problem: 2038
User: gaotianyu1350
Language: C++
Result: Accepted
Time:660 ms
Memory:3048 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 5 * 1e4 + 10;
long long ans[MAXN] = { 0 };
int size[MAXN];
int color[MAXN], cntColor[MAXN];
struct InfoQuery
{
int l, r, idQ, idBlock;
bool operator < (const InfoQuery b) const
{
return (idBlock == b.idBlock && r < b.r) || (idBlock < b.idBlock);
}
}q[MAXN];
int n, m, len;
long long gcd(long long a, long long b)
{
if (!b) return a;
else return gcd(b, a % b);
}
int main()
{
scanf("%d%d", &n, &m);
len = (int)sqrt(n) + 1;
for (int i = 1; i <= n; i++) scanf("%d", &color[i]);
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &q[i].l, &q[i].r);
if (q[i].l > q[i].r) swap(q[i].l, q[i].r);
q[i].idQ = i;
q[i].idBlock = (q[i].l - 1) / len + 1;
size[i] = q[i].r - q[i].l + 1;
}
sort(q + 1, q + 1 + m);
int i = 1;
while (i <= m)
{
int nowBlock = q[i].idBlock;
memset(cntColor, 0, sizeof(cntColor));
for (int j = q[i].l; j <= q[i].r; j++)
ans[q[i].idQ] += 2 * (cntColor[color[j]]++);
for (++i; q[i].idBlock == nowBlock; i++)
{
ans[q[i].idQ] = ans[q[i - 1].idQ];
for (int j = q[i - 1].r + 1; j <= q[i].r; j++)
ans[q[i].idQ] += 2 * (cntColor[color[j]]++);
if (q[i - 1].l < q[i].l)
for (int j = q[i - 1].l; j < q[i].l; j++)
ans[q[i].idQ] -= 2 * (--cntColor[color[j]]);
else
for (int j = q[i].l; j < q[i - 1].l; j++)
ans[q[i].idQ] += 2 * (cntColor[color[j]]++);
}
}
for (int i = 1; i <= m; i++)
{
if (!ans[i])
{
printf("0/1\n");
continue;
}
long long all = (long long)size[i] * (size[i] - 1);
long long d = gcd(ans[i], all);
printf("%lld/%lld\n", ans[i] / d, all / d);
}
}