题目:
2019年南昌邀请赛 - 网络赛G:tsy's number
题意:
多组输入N,求下面式子模 (1<<30) 后的值:
分析:
(1)根据欧拉函数的定义,直接化简为:
预处理 phi(g)*g^3 的前缀和,只要知道 f(n) 的值就可以分块快速计算了,那么 f(n):
g(n) 的值可以 O(1) 直接计算(由于 12 与模数的逆元不存在,但可以处理成只算 3 与模数的逆元),预处理 mul(d)*d^3 的前缀和就可以分块快速计算了;似乎这样复杂度是不对的,但我又不会筛 f(n) 函数,暴力预处理出小部分 f(n) 的值就冲过去了,跑得还特别快,应该是数据水了
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxm = 7e5+75;
const int maxn = 1e7+17;
const int MAXN = 3e4+15;
const int mod = (1<<30);
const int inv3 = 715827883;
int phi[maxn],mul[maxn],prime[maxm],g[maxn],f[maxn],sum[MAXN];
void init(){
phi[1] = mul[1] = 1;int cnt = 0;
for(int i = 2;i < maxn; ++i){
if(!phi[i]){
phi[i] = i-1;mul[i] = -1;
prime[cnt++] = i;
}
for(int j = 0;j<cnt&&1ll*i*prime[j]<maxn; ++j){
if(i%prime[j] == 0){
phi[i*prime[j]] = phi[i] * prime[j];
mul[i*prime[j]] = 0; break;
}
else{
phi[i*prime[j]] = phi[i] * phi[prime[j]];
mul[i*prime[j]] = -mul[i];
}
}
}
for(int i = 1;i < maxn; ++i){
g[i] = g[i-1]+1ll*phi[i]*i%mod*i%mod*i%mod;
if(g[i] >= mod) g[i] -= mod;
f[i] = f[i-1]+1ll*mul[i]*i%mod*i%mod*i%mod;
f[i] = (f[i]+mod)%mod;
}
}
map<int,int> mp;
LL cal(int n){
LL res = 0;
for(int i = 1,last;i <= n; i = last+1){
int d = n/i; last = n/d;
LL tep= 1ll*d*(d+1)/2%mod*(1ll*d*(d+1)/2)%mod*d%mod*(2*d+1)%mod*inv3%mod;
res += (1ll*(f[last]-f[i-1])*tep%mod+mod)%mod;
if(res >= mod) res -= mod;
}
return res;
}
LL FF(int n){
if(n < MAXN) return sum[n];
if(mp.count(n)) return mp[n];
return mp[n] = cal(n);
}
LL solve(int n){
LL res = 0;
for(int i = 1,last;i <= n; i=last+1){
int d = n/i; last = n/d;
res += 1ll*(g[last]-g[i-1]+mod)%mod*FF(d)%mod;
if(res >= mod) res -= mod;
}
return res;
}
int main(){
init(); for(int i = 1;i < MAXN; ++i) sum[i] = cal(i); //暴力预处理部分f(i)的前缀和
int T,n; scanf("%d",&T);
while(T--){
scanf("%d",&n);
printf("%d\n",(int)solve(n));
}
return 0;
}
(2)上面的方法肯定不是正解,正确的姿势应该是:
后面一堆同样可以 O(1) 直接算,中间的 f(T) 函数是一个积性函数可以线性筛直接筛出来,这样就又可以分块加速计算了
f(T) 函数的筛法参考:关于在线性筛中求积性函数
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxm = 7e5+75;
const int maxn = 1e7+17;
const int mod = (1<<30);
const int inv3 = 715827883;
int prime[maxm],vis[maxn],phi[maxn],f[maxn],low[maxn],cnt;
void init(){
f[1] = 1;low[1] = 1;phi[1] = 1;
for(int i = 2;i < maxn; ++i){
if(!vis[i]){
f[i] = 1ll*(i-2)*i%mod*i%mod*i%mod;
prime[cnt++] = i; low[i] = i; phi[i] = i-1;
}
for(int j = 0;j<cnt&&1ll*i*prime[j]<maxn; ++j){
int v = i*prime[j]; vis[v] = 1;
if(i%prime[j] == 0){
phi[v] = phi[i]*prime[j]; low[v] = low[i]*prime[j];
if(i == low[i]) f[v] = 1ll*(phi[v]-phi[i])*v%mod*v%mod*v%mod;
else f[v] = 1ll*f[i/low[i]]*f[prime[j]%mod*low[i]]%mod;
break;
}else{
f[v] = 1ll*f[i]*f[prime[j]]%mod;
low[v] = prime[j]; phi[v] = phi[i]*phi[prime[j]];
}
}
}
for(int i = 1;i < maxn; ++i){ //f(i)函数前缀和
LL tep = 1ll*f[i-1] + f[i];
f[i] = tep>=mod ? tep-mod : tep;
}
}
int solve(int n){
LL res = 0;
for(int i = 1,last;i <= n; i=last+1){
int d = n/i; last = n/d; LL t = 1ll*d*(d+1)/2;
res += (1ll*f[last]-f[i-1]+mod)*t%mod*t%mod*d%mod*(2ll*d+1)%mod;
if(res >= mod) res -= mod;
}
return res*inv3%mod;
}
int main(){
init();
int T,n; scanf("%d",&T);
while(T--){
scanf("%d",&n);
printf("%d\n",solve(n));
}
return 0;
}