看电影(movie):组合数
Description
Input
Output
输出文件共包含T行。
第i行应包含两个用空格隔开的整数A,B,表示输入文件中的第i组数据的答案为A/B。(注意,这里要求将答案化为既约分数)
Sample Input
3
1 1
2 1
2 2
Sample Output
1 1
0 1
3 4
Hint
对于100%的数据 T<=50,N,K<=200,1<=n<=101810^{18}1018,1<=q<=10510^5105
先讲我的摸索出公式心路历程,再讲正常的方法啦,可以自行跳过
没有头绪,推不出式子,怎么办?
模小点打表啊(在机房的时间不够没来得及打代码来打表,所以上课手模超辛酸)
分母是显而易见的,kn,找分子:
4节文化课的成果,再大的点真的模不出来,包括那个5 5
先观察n=k的情况,可以发现是(n+1)n-1
猜测问号处的值是64=1296
第1行,逐项做差:1 1 1 1 1
第2行,也是:5 7 9。再来一次:2 2。
第3行,做差:34 58。再来:24。
发现,做差几次后,会得到常数列。做差的次数为n次。证明这序列是个n-1次函数。
再发现:通过观察右偏下45度的每一条对角线,f(n,k)一定会被(k-n+1)整除。整除之后的商都是次方数。
找到规律:分子是(k-n+1)×(k+1)n-1
接下来考虑约分,显而易见分子上的(k+1)和分母的k不可约分,只有k-n+1需要与k约分
但是可能不止约了一次!如k=18而n=15,那么分别是4和18进行约分
剩下2和9,不要着急往分母分子上乘,因为我们可以拿出另一个k=18,把2约干净。
所以要不断除,直到k的个数不够或者gcd为1。
正常思路:
推出了式子之后,我开始考虑这个式子是什么含义:
我们增加一个座位,表示被干出去的人,最后只需检察这个位置上有没有人即可。
我们把所有作为连成一个环,计算合法方案数即可。
1 #include<cstdio> 2 struct gj{ 3 long long x[40];int ws; 4 friend void operator*=(gj &a,int t){ 5 for(int i=0;i<=a.ws;++i)a.x[i]*=t; 6 for(int i=0;i<=a.ws;++i)a.x[i+1]+=a.x[i]/1000000000000000,a.x[i]%=1000000000000000; 7 if(a.x[a.ws+1])a.ws=a.ws+1; 8 } 9 void print(){ 10 printf("%lld",x[ws]);x[ws]=0; 11 for(int i=ws-1;i>=0;--i)printf("%015lld",x[i]),x[i]=0; 12 ws=0;x[0]=1; 13 } 14 }fz,fm; 15 int gcd(int a,int b){return b?gcd(b,a%b):a;} 16 int k,n,t; 17 int main(){ 18 scanf("%d",&t);fz.x[0]=fm.x[0]=1; 19 while(t--){ 20 scanf("%d%d",&n,&k); 21 if(n>k){puts("0 1");continue;} 22 int ex=k-n+1,tms=n; 23 while(gcd(ex,k)!=1&&tms)fm*=k/gcd(ex,k),ex/=gcd(ex,k),tms--; 24 fz*=ex; 25 for(int i=n-1;i;i--)fz*=k+1; 26 for(int i=tms;i;--i)fm*=k; 27 fz.print();putchar(' ');fm.print();puts(""); 28 } 29 }