两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
2020-2021:teams:legal_string:jxm2001:数论_1 [2020/07/02 19:32] jxm2001 |
2020-2021:teams:legal_string:jxm2001:数论_1 [2020/07/27 22:56] (当前版本) jxm2001 ↷ 页面2020-2021:teams:legal_string:数论_1被移动至2020-2021:teams:legal_string:jxm2001:数论_1 |
||
---|---|---|---|
行 39: | 行 39: | ||
==== 算法简介 ==== | ==== 算法简介 ==== | ||
- | 数论分块用于解决形如 $\sum_{i=1}^n \lfloor \frac ni\rfloor$ 的问题,时间复杂度 $O(\sqrt n)$。 | + | 数论分块用于解决形如 $\sum_{i=1}^n a_if(\lfloor \frac ni\rfloor)$ 的问题,通过预处理 $a_i$ 前缀和,可以把时间复杂度降到 $O(\sqrt n)$。 |
==== 算法思想 ==== | ==== 算法思想 ==== | ||
行 77: | 行 77: | ||
<hidden 查看代码> | <hidden 查看代码> | ||
<code cpp> | <code cpp> | ||
- | #include <cstdio> | + | LL pre_s(int k){return 1LL*k*(k+1)/2;} |
- | #include <iostream> | + | LL cal(int i,int n){ |
- | #include <vector> | + | LL ans=0; |
- | #include <algorithm> | + | int lef=1,rig=0; |
- | #include <cstring> | + | while(lef<=i){ |
- | #include <cctype> | + | rig=min(i,n/(n/lef)); |
- | #include <queue> | + | ans+=(pre_s(rig)-pre_s(lef-1))*(n/lef); |
- | #include <cmath> | + | lef=rig+1; |
- | #define _for(i,a,b) for(int i=(a);i<(b);++i) | + | } |
- | #define _rep(i,a,b) for(int i=(a);i<=(b);++i) | + | return ans; |
- | #define mem(a,b) memset((a),(b),sizeof(a)) | + | |
- | using namespace std; | + | |
- | typedef long long LL; | + | |
- | inline int read_int(){ | + | |
- | int t=0;bool sign=false;char c=getchar(); | + | |
- | while(!isdigit(c)){sign|=c=='-';c=getchar();} | + | |
- | while(isdigit(c)){t=(t<<1)+(t<<3)+(c&15);c=getchar();} | + | |
- | return sign?-t:t; | + | |
} | } | ||
- | inline LL read_LL(){ | + | int main() |
- | LL t=0;bool sign=false;char c=getchar(); | + | { |
- | while(!isdigit(c)){sign|=c=='-';c=getchar();} | + | LL n=read_int(),k=read_int(); |
- | while(isdigit(c)){t=(t<<1)+(t<<3)+(c&15);c=getchar();} | + | enter(n*k-cal(min(n,k),k)); |
- | return sign?-t:t; | + | return 0; |
} | } | ||
- | inline void write(LL x){ | + | </code> |
- | register char c[21],len=0; | + | </hidden> |
- | if(!x)return putchar('0'),void(); | + | |
- | if(x<0)x=-x,putchar('-'); | + | ==== 算法练习 ==== |
- | while(x)c[++len]=x%10,x/=10; | + | |
- | while(len)putchar(c[len--]+48); | + | [[https://www.luogu.com.cn/problem/P2260|洛谷p2260]] |
+ | |||
+ | === 题意 === | ||
+ | |||
+ | 求 \begin{equation}\sum_{i=1}^n\sum_{j=1}^m [i\ne j](n\ mod\ i)\ast (m\ mod\ j)\pmod {19940417}\end{equation} | ||
+ | |||
+ | === 题解 === | ||
+ | |||
+ | 不妨设 $n\le m$。 | ||
+ | |||
+ | \begin{equation}\sum_{i=1}^n\sum_{j=1}^m [i\ne j](n\ mod\ i)\ast (m\ mod\ j)=\sum_{i=1}^n (n\ mod\ i)\ast\sum_{j=1}^m (m\ mod\ j)-\sum_{i=1}^n (n\ mod\ i)\ast (m\ mod\ i)\end{equation} | ||
+ | |||
+ | 前面两项解法同模板题,主要关注最后一项。 | ||
+ | |||
+ | \begin{equation}\sum_{i=1}^n (n\ mod\ i)\ast (m\ mod\ i)=\sum_{i=1}^n n\ast m-n\ast i\ast \lfloor\frac mi\rfloor-m\ast i\ast \lfloor\frac ni\rfloor+i^2\ast\lfloor\frac ni\rfloor\ast\lfloor \frac mi\rfloor\end{equation} | ||
+ | |||
+ | 类似的,也可以用数论分块解决,注意这条式子的最后一项需要同时处理 $n$ 和 $m$ 的分块边界。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int mod=19940417; | ||
+ | LL inv6; | ||
+ | LL exgcd(LL a,LL b,LL &tx,LL &ty){ | ||
+ | if(b==0){ | ||
+ | tx=1,ty=0; | ||
+ | return a; | ||
+ | } | ||
+ | LL re=exgcd(b,a%b,ty,tx); | ||
+ | ty-=a/b*tx; | ||
+ | return re; | ||
} | } | ||
- | inline void space(LL x){write(x),putchar(' ');} | + | LL pre_s_1(int k){return 1LL*k*(k+1)/2%mod;} |
- | inline void enter(LL x){write(x),putchar('\n');} | + | LL s_1(int lef,int rig){return (pre_s_1(rig)-pre_s_1(lef-1))%mod;} |
- | const int MAXP=3e6+5; | + | LL pre_s_2(int k){return 1LL*k*(k+1)%mod*(2*k+1)%mod*inv6%mod;} |
- | LL pre_s(int k){ | + | LL s_2(int lef,int rig){return (pre_s_2(rig)-pre_s_2(lef-1))%mod;} |
- | return 1LL*k*(k+1)/2; | + | LL cal_1(int i,int n){ |
+ | LL ans=0; | ||
+ | int lef=1,rig=0; | ||
+ | while(lef<=i){ | ||
+ | rig=min(i,n/(n/lef)); | ||
+ | ans=(ans+s_1(lef,rig)*(n/lef)%mod)%mod; | ||
+ | lef=rig+1; | ||
+ | } | ||
+ | return ans; | ||
} | } | ||
- | LL cal(int n,int k){ | + | LL cal_2(int i,int n,int m){ |
LL ans=0; | LL ans=0; | ||
int lef=1,rig=0; | int lef=1,rig=0; | ||
- | while(lef<=n){ | + | while(lef<=i){ |
- | rig=min(n,k/(k/lef)); | + | rig=min(i,min(n/(n/lef),m/(m/lef))); |
- | ans+=(pre_s(rig)-pre_s(lef-1))*(k/lef); | + | ans=(ans+s_2(lef,rig)*(n/lef)%mod*(m/lef)%mod)%mod; |
lef=rig+1; | lef=rig+1; | ||
} | } | ||
行 127: | 行 156: | ||
int main() | int main() | ||
{ | { | ||
- | LL n=read_int(),k=read_int(); | + | LL n=read_int(),m=read_int(),ans,t; |
- | enter(n*k-cal(min(n,k),k)); | + | if(n>m) |
+ | swap(n,m); | ||
+ | exgcd(6,mod,inv6,t); | ||
+ | inv6%=mod; | ||
+ | ans=(n*n-cal_1(n,n))%mod*((m*m-cal_1(m,m))%mod)%mod; | ||
+ | ans=(ans-n*n%mod*m%mod)%mod; | ||
+ | ans=(ans+n*cal_1(n,m)%mod)%mod; | ||
+ | ans=(ans+m*cal_1(n,n)%mod)%mod; | ||
+ | ans=(ans-cal_2(n,n,m))%mod; | ||
+ | ans=(ans+mod)%mod; | ||
+ | enter(ans); | ||
return 0; | return 0; | ||
} | } | ||
</code> | </code> | ||
</hidden> | </hidden> |