[关闭]
@xiaoziyao 2020-07-14T12:34:35.000000Z 字数 4159 阅读 1098

P2260 [清华集训2012]模积和解题报告

解题报告


P2260 [清华集训2012]模积和解题报告:

更好的阅读体验

双倍经验:P2834 能力测验

题意

题意:求

数据范围:

分析

这是一道简单的推式子题,但是实现比较恶心。

首先不妨设(如果交换一下就好了)

然后可以用容斥将题目拆成两个部分:

将两个求和拆开:

因为取模很难搞,所以可以用一个性质(取模的定义),将上式转换为:

将括号拆开,可以得到:

此时我们的复杂度已经是了,然而这个复杂度仍然不足以通过本题。

要做这道题需要一个简单的技巧——整除分块。

我们很容易发现很多的值是一样的,且所有的为一个不下降子序列,呈块状分布

通过简单的计算可以得到,对于每个起点为的块,它的值为,终点为,然后我们就可以用的算法计算了:

主要步骤,对于每一个块,它乘号前面前缀和差分得到,即,乘号后面的就是

代码:

  1. inline long long sum1(long long x){
  2. return x*(x+1)%mod*inv2%mod;
  3. }
  4. l=1,sum=n*n%mod;
  5. while(l<=n){
  6. r=n/(n/l);
  7. sum=(sum-(sum1(r)-sum1(l-1)+mod)%mod*(n/l)%mod+mod)%mod;
  8. l=r+1;
  9. }

同样,的值也可以用相同的方法求得。

考虑求,发现用整除分块完成它需要使区间中所有的都满足相同。

可以用类似的方法,将上面代码中的r=n/(n/l);改为r=min(n/(n/l),m/(m/l));,然后就可以直接求值了!

且慢,虽然前三项都很容易求得,但是不是很容易求,因为不好处理。

这里有一个简单的结论:,会在文后证明,现在先给出这一部分的代码:

  1. inline long long sum2(long long x){
  2. return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
  3. }
  4. l=1,tmp3=0;
  5. while(l<=n){
  6. long long a,b,c;
  7. r=min(n/(n/l),m/(m/l));
  8. a=(r-l+1)*n%mod*m%mod;
  9. b=(sum1(r)-sum1(l-1)+mod)%mod*((n/l)*m%mod+(m/l)*n%mod)%mod;
  10. c=(sum2(r)-sum2(l-1)+mod)%mod*(n/l)%mod*(m/l)%mod;
  11. tmp3=(tmp3+a-b+c+mod)%mod;
  12. l=r+1;
  13. }

代码

  1. #include<stdio.h>
  2. const long long mod=19940417,inv2=9970209,inv6=3323403;
  3. long long i,j,k,m,n,l,r,ans,tmp1,tmp2,tmp3;
  4. inline long long min(long long a,long long b){
  5. return a<b? a:b;
  6. }
  7. inline void swp(long long &a,long long &b){
  8. a+=b,b=a-b,a-=b;
  9. }
  10. inline long long sum1(long long x){
  11. return x*(x+1)%mod*inv2%mod;
  12. }
  13. inline long long sum2(long long x){
  14. return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
  15. }
  16. int main(){
  17. scanf("%lld%lld",&n,&m);
  18. if(n>m)
  19. swp(n,m);
  20. l=1,tmp1=n*n%mod;
  21. while(l<=n){
  22. r=n/(n/l);
  23. tmp1=(tmp1-(sum1(r)-sum1(l-1)+mod)%mod*(n/l)%mod+mod)%mod;
  24. l=r+1;
  25. }
  26. l=1,tmp2=m*m%mod;
  27. while(l<=m){
  28. r=m/(m/l);
  29. tmp2=(tmp2-(sum1(r)-sum1(l-1)+mod)%mod*(m/l)%mod+mod)%mod;
  30. l=r+1;
  31. }
  32. l=1,tmp3=0;
  33. while(l<=n){
  34. long long a,b,c;
  35. r=min(n/(n/l),m/(m/l));
  36. a=(r-l+1)*n%mod*m%mod;
  37. b=(sum1(r)-sum1(l-1)+mod)%mod*((n/l)*m%mod+(m/l)*n%mod)%mod;
  38. c=(sum2(r)-sum2(l-1)+mod)%mod*(n/l)%mod*(m/l)%mod;
  39. tmp3=(tmp3+a-b+c+mod)%mod;
  40. l=r+1;
  41. }
  42. ans=(tmp1*tmp2%mod-tmp3+mod)%mod;
  43. printf("%lld\n",ans);
  44. return 0;
  45. }

简单的证明

证明:

(这个式子可以用简单构造法与数学归纳法证明,由于大部分题解都是用的数学归纳法,且用数学归纳法读者自证不难,因此这里使用简单构造法)

由于有这样一个式子:,因此我们可以将这个拆开:

然后乘上

构造一下:

把它们全部展开:

发现括号里的很多项都可以抵消:

证毕。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注