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

本文介绍了一道经典的编程竞赛题目——小Z的袜子问题,重点在于如何通过高效的算法求解从一堆袜子中随机抽取两只颜色相同的概率。文章详细讲解了莫队算法的应用,包括分块排序技巧及其实现细节。

 

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

Description

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(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。

Source

版权所有者:莫涛


  这是一道极为经典的题目。注意到一点,如果我们有计算完[L, R]时的“中间变量”(在本题为每个数出现的次数),那么[L - 1, R]、[L + 1, R]、[L, R - 1]、[L, R + 1]都能够在“中间变量”的“基本操作时间复杂度”(O(1)orO(log n)……)得出。

  例如在此题中,我们如果列出算式,可以轻易发现[L,R]对应的答案通过区间内各颜色数目的平方和快速计算,如果辅助存储区间内各颜色数目,那么O(1)的比邻状态转移就能够实现。

  以此便可介入此题。发现对于一个询问,可以从上一个询问开始通过移动区间得到现有答案。但是,这样很明显不优。状态间的转移即是左端点与右端点的位移,复杂度就是abs(L1 - L2) + abs(R1 - R2)。如果出题人足够邪恶,以至于一次转移是O(n)的,总的算下来O(nq)绝对无法接受。(但我做了实验并没有TLE)。

  当然,有人会说,这道题并没有强制在线,我们可以按照恰当的顺序去解决每个询问。例如,按照左端点从小到大排序,如果左端点相同再右端点从小到大排序。这样确实似乎可以,但出题人也可以乱卡,让右端点乱跳,一次转移同样可以做到O(n)。

  于是,就有人强推曼哈顿最小生成树(可以让顺序完美),但代码复杂度太高,同样也是O(nlogn)的复杂度。我们信息学竞赛不是“完美主义”,很有可能因小失大。

  于是“莫队”(因为比赛常常当队长)说,可以把左端点的排序“模糊处理”,允许在一定区间内的左端点其右端点单调递增。一定区间?这大概就是分块了。我们并不需要去专门维护这个块,只是在排序时用到。假设块长O(k),而一个块右端点最多跳O(n),有O(n/k)个块,故右端点最多跳O(n^2/k)次。左端点每次最多跳O(k)次,故左端点最多跳O(nk)次。

  我们发现,左右端点乱跳次数的乘积是相对一定的,为O(n^3),所以若k=sqrt(n),结果最优,复杂度是O(n*sqrt(n))的。

  但是,话说回来,这类题目为什么不能使用线段树一类的数据结构?理由很简单,颜色太多了。但据说有很强的方法,反正我不会。

  最后说一句,这道题要A特别简单。注:“莫队算法”=分块离线暴力

 

Type直接按照顺序做无脑排序分块排序
Time12228 ms5680 ms

568 ms

 

  当然,可以只sort一次,能开int就开int。904 ms-〉812 ms-〉764 ms-〉568 ms

 

 1 /************************************************************** 
 2     Problem: 2038 
 3  User: Doggu  4  Language: C++  5  Result: Accepted  6  Time:568 ms  7  Memory:2592 kb  8 ****************************************************************/  9 10 #include <cstdio> 11 #include <algorithm> 12 #include <cmath> 13 using namespace std; 14 template<class T>inline void readin(T &res) { 15 static char ch;T flag=1; 16 while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1; 17 res=ch-48; 18 while((ch=getchar())>='0'&&ch<='9')res=(res<<1)+(res<<3)+ch-48; 19 res*=flag; 20 } 21 const int N = 50010; 22 const int Q = 50010; 23 int n, q, K, aa[N], num[N]; 24 struct data{ 25 int L, R, ord; 26 bool operator< (const data &rhs) const {return L/K==rhs.L/K?R<rhs.R:L<rhs.L;} 27 }que[Q]; 28 long long x[N], y[N];inline long long gcd(long long a,long long b) {if(b==0) return a;return gcd(b,a%b);} 29 int main() { 30 readin(n);readin(q);K=floor(sqrt(n)); 31 for( int i = 1; i <= n; i++ ) readin(aa[i]); 32 for( int i = 1; i <= q; i++ ) readin(que[i].L),readin(que[i].R),que[i].ord=i; 33 sort(que+1,que+q+1); 34 int L = 1, R = 0, tot = 0; 35 long long xs, ys, temp; 36 for( int i = 1; i <= q; i++ ) { 37 while(R<que[i].R) { 38 R++; 39 tot+=2*num[aa[R]]+1; 40 num[aa[R]]++; 41  } 42 while(que[i].R<R) { 43 num[aa[R]]--; 44 tot-=2*num[aa[R]]+1; 45 R--; 46  } 47 while(que[i].L<L) { 48 L--; 49 tot+=2*num[aa[L]]+1; 50 num[aa[L]]++; 51  } 52 while(L<que[i].L) { 53 num[aa[L]]--; 54 tot-=2*num[aa[L]]+1; 55 L++; 56  } 57 xs=tot-(R-L+1);ys=(long long)(R-L+1)*(R-L);temp=gcd(xs,ys); 58 x[que[i].ord]=xs/temp;y[que[i].ord]=ys/temp; 59  } 60 //sort(que+1,que+q+1,cmp2); 61 for( int i = 1;i <= q; i++ ) printf("%lld/%lld\n",x[i],y[i]); 62 return 0; 63 }
莫队

 

  

转载于:https://www.cnblogs.com/Doggu/p/bzoj2038.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值