标签:amp 代码 关系 预处理 最大 不能 输出 mil 16px
题意简述:
求1—n中所有数的k次方的和,答案对1234567891取模。
样例输入格式:
一行,两个整数n,k
样例输出格式:
一个整数,即所求的和。
数据范围:n<10^9,k<100
这道题n非常大,即使是O(n)的算法也不能承受,但是此题的k却非常小,这也就提醒由k入手。
首先预处理前k+1个数的k次方的和,如果n<=k+1的话其实就可以直接输出a[0][n]了,不过我觉得出题人不会出这样的数据。
为什么是k+1个数呢?这是为了能处理一些差分的问题。
然后不断地计算每一行的差分,比如这样:
3 2
0 1 5 14
0 1 4 9
0 0 3 5
0 0 0 2
对于每一个a[i][i],其中存储的便是每一个i^k-(i-1)^k-(i-2)^k...-2^k-1^k的值。
即使没有学过导数,也应该能想象,在k>1的情况下,y=x^k这个函数的增长是越来越快的。
假设x1<x2,那么必有 x2^k - (x2-x)^k > x1^k - (x1-x)^k (k>0,x为正整数且x<x1)
然后就能发现,在n个数中任选i个数,所选的数中,最大的数与最小的数之间至少会产生a[i][i]的差值。
组数是一个组合的关系,也就是C(n,i),乘上a[i][i]后加进和中即可。
计算出所有差值并将其累加,就能得到最终结果。
代码来自标程。
#include<cstdio> long long n,k,p=1234567891,tmp=1; long long a[105][105],ans; long long pow(long long x,long long y) { long long re=1; for(;y;y>>=1) { if(y&1) re=re*x%p; x=x*x%p; } return re; } int main() { scanf("%lld%lld",&n,&k); for(int i=1;i<=k+1;i++) a[0][i]=(a[0][i-1]+pow(i,k))%p; for(int i=1;i<=k+1;i++) { for(int j=i;j<=k+1;j++) a[i][j]=(a[i-1][j]-a[i-1][j-1]+p)%p; } for(int i=0;i<=k+1;i++) { ans=(ans+a[i][i]*tmp)%p; tmp=tmp*(n-i)%p*pow(i+1,p-2)%p; } printf("%lld",ans); return 0; }
标签:amp 代码 关系 预处理 最大 不能 输出 mil 16px
原文地址:http://www.cnblogs.com/zeroform/p/7603046.html