题目描述
作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。
然而数据中有L=R的情况,请特判这种情况,输出0/1。
题解
可以说是莫队算法板子题了。
一直区间 [ l , r ] [l,r] [l,r]的答案是 ∑ C c n t x 2 C r − l + 1 2 = ∑ c n t x 2 − ∑ c n t x ( r − l ) ∗ ( r − l + 1 ) \frac{\sum C_{cnt_x}^{2}}{C_{r-l+1}^{2}}\ =\ \frac{\sum cnt_x^2-\sum cnt_x}{(r-l)*(r-l+1)} Cr−l+12∑Ccntx2 = (r−l)∗(r−l+1)∑cntx2−∑cntx.
因此我们只要统计每一个答案中分子的数即可。
如果我们一直[l,r]→[l,r+1],改变的答案计算方式可以是:减去原来的答案,再加上新增加的答案。具体的讲就是: a n s − c n t x 2 + ( c n t x + 1 ) 2 − 1 ans-cnt_x^2+(cnt_x+1)^2-1 ans−cntx2+(cntx+1)2−1.如果是[l,r]→[l,r-1],新的答案是: a n s − c n t x 2 + ( c n t x − 1 ) 2 + 1 ans-cnt_x^2+(cnt_x-1)^2+1 ans−cntx2+(cntx−1)2+1.
约分操作只要同时除以分子和分母的最大公因数即可。
代码如下:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
struct node {
int L, R, id, p;
friend bool operator < (node p1, node p2) {
if (p1.p == p2.p) return p1.R < p2.R;
return p1.p < p2.p;
}
};
int n,m,T;
int ans = 0;
int a[200000];
node q[200000];
int cnt[200000];
pair<int,int>Ans[200000];
inline int read(void)
{
int s = 0, w = 1;char c = getchar();
while (c<'0' || c>'9') {if (c == '-') w = -1; c = getchar();}
while (c>='0' && c<='9') s = s*10+c-48,c = getchar();
return s*w;
}
void del(int x)
{
ans = ans - cnt[x] * cnt[x] + (cnt[x]-1) * (cnt[x]-1) + 1;
cnt[x] --;
}
void ins(int x)
{
ans = ans - cnt[x] * cnt[x] + (cnt[x]+1) * (cnt[x]+1) - 1;
cnt[x] ++;
}
int gcd(int a,int b)
{
if (b == 0) return a;
return gcd(b,a%b);
}
signed main(void)
{
freopen("2012.in","r",stdin);
freopen("2012.out","w",stdout);
n = read();
m = read();
T = sqrt(n);
for (int i=1;i<=n;++i) a[i] = read();
for (int i=1;i<=m;++i)
{
q[i].L = read();
q[i].R = read();
q[i].id = i;
q[i].p = (q[i].L+T-1) / T;
}
sort(q+1, q+m+1);
ans = 0;
cnt[a[1]] = 1;
int l = 1;
int r = 1;
for (int i=1;i<=m;++i)
{
while (l < q[i].L) del(a[l++]);
while (l > q[i].L) ins(a[--l]);
while (r < q[i].R) ins(a[++r]);
while (r > q[i].R) del(a[r--]);
int k = (q[i].R - q[i].L) * (q[i].R - q[i].L + 1);
if (ans == 0) Ans[q[i].id] = make_pair(0,1);
else Ans[q[i].id] = make_pair(ans/gcd(ans,k), k/gcd(ans,k));
}
for (int i=1;i<=m;++i) printf("%lld/%lld\n", Ans[i].first, Ans[i].second);
return 0;
}