[雅礼中学2005分区联赛模拟试题]求和
[题目]
求和(Sum.pas/exe)
【题目描述】
高斯在他还是小P孩的时候就求出了1+2+…+n=n*(n+1)/2;
LT在他还是小P孩的时候就知道1/(1*2)+1/(2*3)+…+1/((n-1)*n)=1-1/n;
现在,在你还是小P孩的时候,你要求出
【输入】
输入两个整数n,m。
【输出】
输出占两行,第一行一个整数X,第二行整数Y,表示S=X/Y,且X,Y互质。
【样例输入】
1 2
【样例输出】
1
2
【数据范围】
m>1,n>0;
50%的数据满足n<=50;
100%的数据满足n+m<=500。
[成绩]
标题:求和 | |||
文件名:sum.cpp | |||
编号 | 结果 | 得分 | 有效用时 |
1 | 正确 | 10 | 0.01 |
2 | 正确 | 10 | 0.01 |
3 | 正确 | 10 | 0.01 |
4 | 正确 | 10 | 0.03 |
5 | 正确 | 10 | 0.01 |
6 | 正确 | 10 | 0.03 |
7 | 正确 | 10 | 0.03 |
8 | 正确 | 10 | 0.04 |
9 | 正确 | 10 | 0.01 |
10 | 正确 | 10 | 0.03 |
总分:100 | |||
有效用时:0.21 |
[报告]
首先,对
进行处理:
那么
由于1×2×...×(m-1)必然整除(n+1)×(n+2)×...×(n+m-1)
——这个可以证明,网上面遍地都是——令k=((n+1)×(n+2)×...×(n+m-1))/(1×2×...×(m-1)),那最后的答案就是
在某种情况下,分数线上下可能都是高精度数。怎么办?高精度GCD!——不过这个非常不现实。
那怎么办?注意到分母是一串数相乘,分子未知。联想到小学的时候求GCD的方法——试除法!也就是说,枚举分母里所有数的所有因子,尝试进行约分。这样就可以解出S了。
至此,这道题圆满解决。具体实现请看程序,可能部分实现比较怪异,但都经过鄙人的伪证明的……
[程序]
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <string>
#define N 500
#define L 1000
#define bx
1000000
// 6位位压
#define wx 6
using namespace std;
ifstream fin ("sum.in");
ofstream fout ("sum.out");
long n,m;
long
x[N+10],x2[N+10];
// 乘积即为分母
long
k[L+10];
// 分子
static inline void fouot(long a[]);
static inline void ouot(long a[]);
static inline long gcd(long a,long b)
{
if (b==0) return a;
else return gcd(b,a%b);
}
static inline void multi(long a[],long
b=1)
// 高*单
{
for (long i=L;i>=1;i--)
a[i]*=b;
for (long i=L;i>=1;i--)
{
a[i-1]+=a[i]/bx;
a[i]%=bx;
}
}
static inline void decre(long a[],long
b=1)
// 高-单
{
a[L]-=b;
for (long i=L;i>=1;i--)
while
(a[i]<0)
{
a[i-1]--;
a[i]+=bx;
}
}
static inline long modit(long a[],long
b=1)
// 高%单
{
long ret=0;
long i=1;
while
(i<=L&&a[i]==0)
i++;
// 直接忽略前面的0
for (long qx;i<=L;i++)
{
qx=ret*bx+a[i];
ret=qx%b;
}
return ret;
}
static inline void divit(long a[],long
b=1)
// 高/单
{
long i=1,c=0;
while
(i<=L&&a[i]==0)
i++;
// 直接忽略前面的0
for (long qx;i<=L;i++)
{
qx=c*bx+a[i];
c=qx%b;
a[i]=qx/b;
}
}
int main(int argc, char *argv[])
{
fin >> n
>> m;
memset(k,0,sizeof(k));
k[L]=1;
memset(x,0,sizeof(x));
for (long i=1;i<m;i++)
multi(k,x[i]=i+n);
memcpy(x2,x,sizeof(x2));
for (long i=2,qx;i<m;i++)
{
long px=i;
for (long
j=1;j<m&&px>1;j++)
while
((px>1)&&((qx=gcd(px,x[j]))>1))
// 可以约分
{
divit(k,qx);
x[j]/=qx;
px/=qx;
}
}
memcpy(x,x2,sizeof(x));
decre(k,1);
x[m]=m-1;
for (long
i=1,qx,px;i<=m;i++)
if
(x[i]>1)
{
if
((qx=modit(k,x[i]))==0)
// 完全整除
{
divit(k,x[i]);
x[i]=1;
}else if
((px=gcd(qx,x[i]))>1) //
不完全整除
{
divit(k,px);
x[i]/=px;
}