Description
由于出题人懒得写背景了,题目还是简单一点好。
输入一个整数n和一个整数p,你需要求出∑ni=1∑nj=1i×j×gcd(i,j)(modp)∑i=1n∑j=1ni×j×gcd(i,j)(modp)
对于20%的数据, n≤1000n≤1000 。
对于30%的数据, n≤5000n≤5000 。
对于60%的数据, n≤106n≤106 ,时限1s。
对于另外20%的数据, n≤109n≤109 ,时限3s。
对于最后20%的数据, n≤1010n≤1010 ,时限6s。
对于100%的数据,5×108≤p≤1.1×1095×108≤p≤1.1×109 且p为质数。
Solution
感觉对杜教筛的理解又深一点点
ans=∑i=1n∑j=1ni×j×gcd(i,j)=∑d=1nd3∑i=1⌊nd⌋∑j=1⌊nd⌋i×j×[gcd(i,j)==1]ans=∑i=1n∑j=1ni×j×gcd(i,j)=∑d=1nd3∑i=1⌊nd⌋∑j=1⌊nd⌋i×j×[gcd(i,j)==1]
第一波操作照例是枚举gcd,然后再套μ得到
ans=∑d=1nd3∑x=1⌊nd⌋x2μ(x)S2(⌊ndx⌋)ans=∑d=1nd3∑x=1⌊nd⌋x2μ(x)S2(⌊ndx⌋)
其中S(n)=∑ni=1iS(n)=∑i=1ni
令T=dxT=dx枚举TT得到
有一个结论是∑d|Tμ(d)Td=φ(T)∑d|Tμ(d)Td=φ(T),知道了这个就可以继续往下推惹
ans=∑T=1nS2(⌊nT⌋)T2φ(T)ans=∑T=1nS2(⌊nT⌋)T2φ(T)
这里已经可以分块求,但是还需解决后面的前缀和问题,考虑杜教筛
设f(i)=i2φ(i)f(i)=i2φ(i),g(i)=i2g(i)=i2,h(n)=∑ni=1f(i)h(n)=∑i=1nf(i)
容易得到
∑i=1n∑d|ig(d)×f(id)=∑i=1n∑d|id2×(id)2×φ(id)=∑i=1ni2∑d|iφ(d)=∑i=1ni3∑i=1n∑d|ig(d)×f(id)=∑i=1n∑d|id2×(id)2×φ(id)=∑i=1ni2∑d|iφ(d)=∑i=1ni3
同时 ∑i=1n∑d|ig(d)×f(id)=∑i=1n∑d|id2×(id)2×φ(id)=∑d=1nd2∑i=1⌊nd⌋i2×φ(i)=∑d=1nd2×h(⌊nd⌋)∑i=1n∑d|ig(d)×f(id)=∑i=1n∑d|id2×(id)2×φ(id)=∑d=1nd2∑i=1⌊nd⌋i2×φ(i)=∑d=1nd2×h(⌊nd⌋)
当i=1i=1时拉出来得到
h(n)=∑i=1ni3−∑d=2nd2h(⌊nd⌋)h(n)=∑i=1ni3−∑d=2nd2h(⌊nd⌋)
那么就可以杜教筛直接上了
需要指出的是,∑ni=1i3=(∑ni=1i)2∑i=1ni3=(∑i=1ni)2,我怕不是小学奥数题都不会了
这题的模数不一定是质数因此不能用费马小定理求逆元,考虑到n不是特别大可以直接除
Code
#include <stdio.h>
#include <map>
typedef long long LL;
const int N=10000000;
std:: map <LL,LL> map;
int prime[N+5],MOD;
bool not_prime[N+5];
LL f[N+5];
void pre_work() {
f[1]=1;
for (int i=2;i<=N;i++) {
if (!not_prime[i]) {
prime[++prime[0]]=i;
f[i]=i-1;
}
for (int j=1;i*prime[j]<=N&&j<=prime[0];j++) {
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) {
f[i*prime[j]]=f[i]*prime[j];
break;
}
f[i*prime[j]]=f[i]*f[prime[j]];
}
}
for (int i=1;i<=N;i++) f[i]=(f[i-1]+f[i]*i%MOD*i%MOD)%MOD;
}
LL S(LL n) {
n=n%MOD;
return (LL)n*(n+1)/2%MOD;
}
LL S2(LL n)
{
n=n%MOD;
if ((LL)n*(n+1)%3==0) return (LL)n*(n+1)/6%MOD*(n*2+1)%MOD;
else return (LL)(n*2+1)/3*((LL)n*(n+1)/2%MOD)%MOD;
}
LL get_sum(LL n) {
if (n<=N) return f[n];
if (map[n]) return map[n];
LL ret=S(n); ret=ret*ret%MOD;
for (LL i=2,j;i<=n;i=j+1) {
j=n/(n/i);
ret=(ret-(S2(j)-S2(i-1)+MOD)%MOD*get_sum(n/i)%MOD+MOD)%MOD;
}
return map[n]=ret;
}
void solve(LL n) {
LL ans=0;
for (LL i=1,j;i<=n;i=j+1) {
j=n/(n/i);
LL sum1=S(n/i);
ans=(ans+(sum1*sum1)%MOD*(get_sum(j)-get_sum(i-1)+MOD)%MOD)%MOD;
}
printf("%lld\n", (ans+MOD)%MOD);
}
int main(void) {
LL n; scanf("%d%lld",&MOD,&n);
pre_work();
solve(n);
return 0;
}