NYOJ 420 p次方求和

p次方求和

时间限制: 1000 ms | 内存限制: 65535 KB
难度:3
描述
一个很简单的问题,求1^p+2^p+3^p+……+n^p的和。
输入
第一行单独一个数字t表示测试数据组数。接下来会有t行数字,每行包括两个数字n,p,
输入保证0<n<=1000,0<=p<=1000。
输出
输出1^p+2^p+3^p+……+n^p对10003取余的结果,每个结果单独占一行。
样例输入
2
10 1
10 2
样例输出  
55
385

纠结好久的题终于AC了,虽然不是这道题的正确解法。原来的代码总是超时,就不再多说了。

 

AC的代码(二分):

 

#include<stdio.h>
int pow(int a,int b,int m)
{
    int x;
    long long ans;
    if (b==1)
        return a%m;
    x=pow(a,b/2,m);//递归调用
    ans=(long long)x*x%m;
    if (b%2==1)
        return ans=ans*a%m;
   return (int)ans;
}
int main()
{
    int k,n,p,sum,an;
    scanf("%d",&k);
    while (k--)
    {
        scanf("%d%d",&n,&p);
        if (p==0)
            printf("%d\n",n%10003);
        else
        {
            sum=0;
            for (int i=1;i<=n;++i)
            {
                an=pow(i,p,10003);
                sum=((sum%10003)+(an%10003))%10003;
            }
            printf("%d\n",sum);
        }
    }
   return 0;
}


最优代码:

 

 

#include <stdio.h>
int main()
{
    int a[1005][1005];
    //用二维数组a[i][j]表示j的i次方
    int i,j,k,l,m,n;
    for(i=0; i<1001; i++)
        a[0][i]=1;
    for(i=1; i<1001; i++)
        for(j=1; j<1001; j++)
            a[i][j]=a[i-1][j]*j%10003;
    //a[i][j]表示j的i次方,等于j的i-1次方乘以i
    scanf("%d",&l);
    while(l--)
    {
        scanf("%d%d",&m,&n);
        for(i=1,k=0; i<=m; i++)
            k=k+a[n][i];
        printf("%d\n",k%10003);
    }
    return 0;
}


 


 

出题者对我们说:

 

[NYOJ420]p次方求和的正解,同学们不要再水了……

这个题目是我出的,出这个题目是很早前对这个问题就有点研究了,不过最近算是彻底的搞清楚了,感觉结论还是很漂亮的,就出了这个题目:题意很简单,但是完全弄会它,你真的可以学会很多东西。主要是我人心太软了(自恋),弄的数据都太水了,结果大家的AC代码没有一个是用正解思路写的,当然不是说不对,而是,这样你会错过很多知识。

好开始正题,首先这个问题涉及比较的多,包括高等代数的线性空间,组合数学的stirling数,和一些简单的排列组合公式,这些东西的综合应用才给出了这个问题的漂亮解法。

好吧,先说最为基本的,也是最主要的思路:对于n^p,我现在考虑它的另一种表示形式:n^p=k1*C(n,p)+k2*C(n,p-1)+······+kp*C(n,1);这个一定是有的,而且这一组(k1,k2,k3……kp)也是唯一确定的,至于为什么,如果你真要深究请参阅线性空间的基,过度矩阵……等相关概念可以很容易给出这样的结论,这里就不做说明了。

为什么要给出这样的表示形式呢?这是应为组合公式里有一个很简单的结论:C(k,k)+C(k+1,k)+C(k+2,k)+······+C(n,k)=C(n+1,k+1);这个也很好证明,多种方法,也不介绍了。

于是乎,对于每一个n^p都可以表示为n^p=k1*C(n,p)+k2*C(n,p-1)+······+kp*C(n,1);再用C(k,k)+C(k+1,k)+C(k+2,k)+······+C(n,k)=C(n+1,k+1);就可以很快的求和了。

现在,还有一个问题,(k1,k2,k3……kp)到底要怎么确定呢?对于较低次数的我们可以很容易分析出这些系数,例如n^2=2*C(n,2)+C(n,1);n^3=6*C(n,3)+6*C(n,2)+C(n,1);……;那如果次数较高呢?这里用到了些组合数学中第二类Stirling数,不细说了,自己看百科去吧。

有如下结论:ki=(p+1-i)!*S(p,i),于是乎,这个问题就彻底明朗了,呵呵。

最后把这些个公式组合下,化简下,最后的结论或许会又简洁了不少。

代码还没有写出来,这个文章主要是将这些知识的,至于代码那是大家的强项了,应该很好写出来吧。

HiT说些自己的感想,数学的确很枯燥,最近我们在学线性空间,那些都是很抽象的东东,你就算刚觉自己把那些公式定理都搞清楚了,但到头来你却悲剧的发现,你连这些东西是个啥都搞不清,matrix67曾经说过最可悲的是有些定理你知道,你会用,你会证明…… 但是你却缺少对它很直观的了解,那么学线性空间你会经常陷入到这种悲哀中,但是这些却又都是十分有用的,比如这个问题的求解,你怎么就能会料到,它能和线性空间扯上关系呢?但它的确是有的,而且这些结论在线性空间里是那么的平常!

也学数学的优雅就在于此:当你遇到一个很复杂的问题时,你回首一看,宛然笑道,这些问题N久之前就研究清楚了,而你当时学习这些的时候却完全不知道它究竟会有什么用处……

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值