[雅礼中学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 正确100.01
2 正确100.01
3 正确100.01
4 正确100.03
5 正确100.01
6 正确100.03
7 正确100.03
8 正确100.04
9 正确100.01
10 正确100.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;
   }
  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值