题目描述
作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。
然而数据中有L=R的情况,请特判这种情况,输出0/1。
输入输出格式
输入格式:输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。
输出格式:包含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
说明
30%的数据中 N,M ≤ 5000;
60%的数据中 N,M ≤ 25000;
100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。
Solution:
用这道题当作莫队的模板吧。(我这里不详细深究莫队的原理,只谈及一下自己的理解)莫队
莫队其实就是优化的暴力。其实只有4个$while$循环,复杂度就体现在这4个循环的指针移动次数,而将进行分块并块内排序后能降低指针移动次数,起到优化作用。我们离线操作,读入查询的所有区间,分为$sqrt{n}$块,每块中按$r$升序排列。
在每块中进行暴力指针移动,对于指针$l,r$各自只存在左移和右移两种情况,每次移动1步,便重新维护一下信息(本题中的分子),处理出答案后保存一下,最后输出就$OK$了。
回到本题,我们设$s[i]$表示$i$数字在区间$[l,r]$中出现的次数,容易得出区间$[l,r]$的抽到概率为:
$$frac{sum s[i]*(s[i-1]-1)}{(l-r+1)(l-r)}=frac{sum s[i]^2-sum s[i]}{(l-r+1)(l-r)}=frac{sum s[i]^2-(l-r+1)}{(l-r+1)(l-r)}$$
于是我们只需维护一下$s[i]^2$,然后在每次指针移动后用分子减去原来的$s[i]^2$,更新$s[i]$,再加上新的$s[i]^2$,便求出新的分子,至于分母就是$(r-l+1)(r-l)$。
然后求出分子和分母的最大公约数,约分一下并保存答案,最后输出就完美的解决了。
代码:
#include<bits/stdc++.h> #define il inline #define ll long long using namespace std; const int N=50005; ll n,m,pos[N],a[N],ans,s[N],fen[N][2]; struct data{ ll l,r,id; }t[N]; il ll gi() { ll a=0;char x=getchar();bool f=0; while((x<'0'||x>'9')&&x!='-')x=getchar(); if(x=='-')x=getchar(),f=1; while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); return f?-a:a; } il ll gcd(ll x,ll y){return y?gcd(y,x%y):x;} il bool cmp1(data a,data b){return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;} il void add(ll p,ll val) { ans-=s[a[p]]*s[a[p]]; s[a[p]]+=val; ans+=s[a[p]]*s[a[p]]; } int main() { n=gi(),m=gi(); for(int i=1;i<=n;i++)a[i]=gi(); int ss=int(sqrt(n)); for(int i=1;i<=n;i++)pos[i]=i/ss; for(int i=1;i<=m;i++)t[i].l=gi(),t[i].r=gi(),t[i].id=i; sort(t+1,t+1+m,cmp1); for(int i=1,l=1,r=0;i<=m;i++){ while(r<t[i].r)r++,add(r,1); while(r>t[i].r)add(r,-1),r--; while(l<t[i].l)add(l,-1),l++; while(l>t[i].l)l--,add(l,1); if(t[i].l==t[i].r){fen[t[i].id][0]=0,fen[t[i].id][1]=1;continue;} fen[t[i].id][0]=ans-(t[i].r-t[i].l+1); fen[t[i].id][1]=(ll)(t[i].r-t[i].l+1)*(t[i].r-t[i].l); ll k=gcd(fen[t[i].id][0],fen[t[i].id][1]); fen[t[i].id][0]/=k,fen[t[i].id][1]/=k; } for(int i=1;i<=m;i++)printf("%lld/%lld ",fen[i][0],fen[i][1]); return 0; }