[FZYZOJ 1002] 雨天
P1002 -- 雨天
时间限制:1000MS 内存限制:65536KB无 无
Description
规则是这样的:每位姿迷可以抽到一张奖券。奖券上写有1到M这M个自然数。姿迷可以在这M个数中任意选取N个不同的数打圈。每个姿迷只能买一张奖券,不同的奖券上的选择不同。
每次抽奖将抽出两个自然数X和Y。如果某人拿到的奖券上,所选N个自然数的倒数和,恰好等于X/Y,则他将免费获得一张CD《My Story Your Song》。 现在,已知抽奖结果X和Y。作为燕姿的fans,你的任务是:求出必须准备多少CD,才能保证支付所有获奖者。 对于所有数据,N<=12,M<=60 X<=25,Y<=25 且对于同一种选数,syz只用支付一盘CD
Input Format
输入有且仅有一行,就是用空格分开的四个整数N,M,X,Y。
Output Format
输出有且仅有一行,即所需准备的CD数量。
Sample Input
2 4 3 4
Sample Output
1
Hint
好题!
【题解】
看到本题n、m、x、y都十分小,所以不需要使用分数类,直接用double,注意精度问题,判断相等用1e-9作为标准。
而且呢,还可以直接搜索,但是原本搜索的时间复杂度的max为C(60,12),但是我们可以加入如下剪枝:
1. 如果当前得到的数已经比x/y大,那么return
2. 如果当前得到的数加上最大的数乘还剩下选的次数仍比x/y小,那么return
3. 如果当前得到的数加上最小的数乘还剩下选的次数仍比x/y大,那么return
第4条,也是重要的一条,大神讲的:
4. 假设当前总和为a/b(a,b互质),那么a/b+1/p=(ap+b)/(bp),可知p一定会留在分母不能被约去,因为b一定不小于2,所以当p为比25大的质数时,1/p一旦被加入总和,分母便一定大于25,超过了题目y的范围。我们可以不予考虑这些质数;
然后根据神牛的指导,发现了一些规律,呈现在代码里了
= =其实我也没有弄懂|||
然后就AC了,0.107s,还排前几名。orz
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 double tar; 4 double eps = 1e-9; 5 double minuseps = -eps; 6 bool used[61]; 7 int cnt=0; 8 int n,m,x,y; 9 inline double fabs(double a) {return a>0?a:-a;} 10 void tryy(int step, double now, int last) { 11 //cout<<step<<' '<<now<<' '<<last<<endl; 12 if (step == n+1) { 13 if (now<=eps&&now>=minuseps) cnt++; 14 return ; 15 } 16 if (now <= minuseps) return; 17 if (now - (n-step+1)*1.0/(last+1) >= eps) return; //printf("1"); 18 if (now - (n-step+1)*1.0/m <= minuseps) return; //printf("1"); 19 for (int i=last+1;i<=m;++i) if(used[i]) tryy(step+1, now-1.0/i, i); 20 //printf("1"); 21 } 22 int main() { 23 scanf("%d%d%d%d",&n,&m,&x,&y); 24 tar=1.0*x/y; 25 memset(used,0,sizeof(used)); 26 for (int i=2;i<=sqrt(m);++i) 27 if(!used[i]) for (int j=i*2;j<=m;j+=i) used[j]=1; 28 for (int i=1;i<=11;++i) used[i]=true; 29 used[25]=used[26]=used[27]=used[32]=used[34]=used[38]=used[39]=used[44]=used[46]=used[49]=used[50]=0; 30 tryy(1,tar,0); 31 printf("%d\n",cnt); 32 return 0; 33 }