题目:
分析:
题意是比较简洁的,我们先不考虑取整的问题(精度问题和取mod问题也先放放),这题应该怎么做.
首先m很小,我们从m下手下手,首先观察f(i,j)会发现f(i+j,j)=f(i,j),首先我们要知道:(p,q)=1,那么(p+q,q)=1,证明如下:
设存在p,q互质且p+q,q不互质,那么就有p+q=k1*x,q=k2*x(x!=1),那么有p=(k1-k2)x,不与q互质,与假设矛盾,故原式得证.
然后(i,j)=(i+j,j)是类似的,设i=px,j=qx,p,q互质,那么(i+j,j)=(px+qx,qx)=x(p+q,q)=x,所以(i,j)=(i+j,j)
于是原函数里的x是相等的,对于c,第一次操作将会把x变成j,y变成i%j,之后操作就相同了,自然也是相等的
于是有f(i,j)=f(i+j,j)
于是:i*j/f(i,j)+(i+j)*j/f(i+j,j)+(i+2*j)*j/f(i+2*j,j)+...+(i+k*j)*j/f(i+k*j,j)=((i*j)+(i*j+j*j)+(i*j+2*j*j)+...+(i*j+k*j*j))/f(i,j)(i<=j)
发现上面是等差,下面是一个可以算的数,于是我们就可以通过一次求和公式把n的一堆数字加进来,对于每一个j我们只需要遍历一个j的完全剩余系,求出和来就可以了,复杂度m*m*logm(log求f函数)
然后如何取整,由于是对里面取整,先算出来再取很可能进位不该进位的1,所以我们考虑换一种办法,现在不能以j分界了,但是我们想想,出现小数的原因是c,如果没有c,i*j/(x*x)不会是小数,但是我们如果c个c个的j去增加,那么取整之后仍然是等差(相当于舍掉的数相同),那么我们就可以这样去处理,发现复杂度还是m*m*logm,没有问题.
代码:
#include <cstdio> long long mod; long long gcd1; long long gcd2(long long a,long long b){//求gcd return b?gcd2(b,a%b):a; } long long gcdc(long long x,long long y){//求c long long t; long long c=0; while(y>0){ c++; t=x%y; x=y; y=t; } return c; } const int maxn=666+10; long long gdc[maxn][maxn]; long long gd2[maxn][maxn]; int main(){ int t; scanf("%d",&t); for(int i=1;i<=666;i++) for(int j=1;j<=666;j++){ gdc[i][j]=gcdc((long long)i,(long long)j); gd2[i][j]=gcd2((long long)i,(long long)j); } for(int jsjs=1;jsjs<=t;jsjs++){ long long n,m,ans=0; scanf("%lld%lld%lld",&n,&m,&mod); for(int i=1;i<=m;i++) for(int j=1;j<=i;j++){ if(j>n) break; long long c=gdc[j][i]; long long gd=gd2[j][i]; long long f=c*gd*gd;//f函数 for(int k=0;k<c;k++){ if(j+k*i>n) break; long long sx=(j+k*i)*i/f//首项; long long xs=(n-j-k*i)/(c*i)+1;//项数 long long gc=c*i*i/f;//公差 ans+=sx*xs+xs*(xs-1)/2%mod*gc;//数列求和 ans%=mod; } } printf("%lld ",ans); } return 0; }