bzoj 2038(莫队)

2038: [2009国家集训队]小Z的袜子(hose)

Time Limit: 20 Sec   Memory Limit: 259 MB
Submit: 6280   Solved: 2901
[ Submit][ Status][ Discuss]

Description

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1N编号,然后从编号LR(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

Input

输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。

Output

包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)

Sample Input

6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6

Sample Output

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。


解题思路:莫队其实就是改变询问的顺序并且用分块对应的块的编号作为关键字进行排序。然后暴力。。。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int n,m,now1,now2;
long long sum;
struct ss
 {
  int x,y,q;
 }a[50001];
int pos[50001];
long long zan[50001],zong[50001],ans[50001],c[50001];


inline long long read()
{
char y; long long x=0,f=1; y=getchar();
while (y<'0' || y>'9') {if (y=='-') f=-1; y=getchar();}
while (y>='0' && y<='9') {x=x*10+int(y)-48; y=getchar();}
return x*f;
}


bool cmp(ss x1,ss y1)
 {
  if (pos[x1.x]!=pos[y1.x]) return pos[x1.x]<pos[y1.x];else 
  return pos[x1.y]<pos[y1.y];
 }


long long gcd(long long x,long long y)
 {
  if (y==0)return x;else return gcd(y,x%y);
 }


void prepare()
 {
  sum=0;
  memset(zan,0,sizeof(zan));
  for (int i=a[1].x; i<=a[1].y;++i)
  zan[c[i]]++;
  for (int i=1;i<=n;++i)
  {
  sum+=zan[i]*(zan[i]-1)/2;
 }
ans[a[1].q]=sum; 
 }


int main()
{
freopen("main.in","r",stdin);
freopen("me.out","w",stdout);
n=read(); m=read();
for (int i=1;i<=n;++i){c[i]=read();}
int len=int(sqrt(n));
for (int i=1;i<=n;++i) pos[i]=(i-1)/len+1;
for (int i=1;i<=m;++i)
{
int x=read(); int y=read();
if (y<x) swap(x,y);
a[i].x=x; a[i].y=y; a[i].q=i;
zong[i]=(y-x+1)*(y-x)/2;
}
sort(a+1,a+m+1,cmp);
prepare(); 
now1=a[1].x; now2=a[1].y;
for (int i=2;i<=m;++i)
{
while (now1>a[i].x)
{
--now1;sum+=zan[c[now1]];++zan[c[now1]];
 }
while (now1<a[i].x)
{
sum-=zan[c[now1]]-1;--zan[c[now1]];++now1;
}
while (now2>a[i].y)
{
   sum-=zan[c[now2]]-1;--zan[c[now2]];--now2;
}
while (now2<a[i].y)
{
++now2;sum+=zan[c[now2]];++zan[c[now2]];
}
ans[a[i].q]=sum;
}
for (int i=1;i<=m;++i)
{
if (ans[i]==0)
{
 printf("0/1\n");
}else
 { long long opp=gcd(ans[i],zong[i]);
       printf("%lld/%lld\n",ans[i]/opp,zong[i]/opp);
     }
}
return 0;
}

你可能感兴趣的:(bzoj 2038(莫队))