problem
计算
1≤n≤10111≤n≤1011
思路
解法一
首先写作∑ni=1∑ij=1(n−⌊nij⌋∗ij)=∑ni=1∑ij=1n−∑ni=1∑ij=1⌊nij⌋∗ij∑i=1n∑j=1i(n−⌊nij⌋∗ij)=∑i=1n∑j=1in−∑i=1n∑j=1i⌊nij⌋∗ij
对于∑ni=1∑ij=1n∑i=1n∑j=1in不用说了,对于∑ni=1∑ij=1⌊nij⌋∗ij=∑ni=1i∗∑ij=1⌊⌊ni⌋j⌋∗j∑i=1n∑j=1i⌊nij⌋∗ij=∑i=1ni∗∑j=1i⌊⌊ni⌋j⌋∗j
对于第二维,所求是j∈[1,i][1,i] 我们这里求[1,n][1,n] 由对称性可知[1,i][1,i]的两倍减去[i=j][i=j]的情况即为所求
所以现在目标是∑ni=1i∗∑nj=1⌊⌊ni⌋j⌋∗j∑i=1ni∗∑j=1n⌊⌊ni⌋j⌋∗j
由于⌊ni⌋⌊ni⌋ 只有O(n−−√)O(n)种取值,我们枚举⌊ni⌋⌊ni⌋的值,对应一段ii区间,可知对于这一段,第二维值是相同的。
对于第二维的计算类似,也是分块思想。所以写一个solve函数用于求解f(n)=∑ni=1⌊ni⌋∗if(n)=∑i=1n⌊ni⌋∗i 即可。
时间复杂度 O(∑n√i=1ni−−√)=O(n34)O(∑i=1nni)=O(n34) (只有O(n−−√)O(n)种取值) 注:∑nxi=1ni−−√∑i=1nxni的计算式: n(12+12x)n(12+12x)
TLE 1e11大概要跑37s
解法二
定义g(n)=f(n)−f(n−1)=∑ni=1i∗(⌊ni⌋−⌊n−1i⌋)=∑d|ndg(n)=f(n)−f(n−1)=∑i=1ni∗(⌊ni⌋−⌊n−1i⌋)=∑d|nd
对于g(n)g(n),即一个数的因子和,我们可以O(n)O(n)求解出其前nn项,对于本题 ,我们处理出gg的前项 ,然后求个前缀和就得到了f(n)f(n) ,这一部分的时间复杂度是O(n23)O(n23)
所以对于∑ni=1i∗∑nj=1⌊⌊ni⌋j⌋∗j∑i=1ni∗∑j=1n⌊⌊ni⌋j⌋∗j,当⌊ni⌋<n23⌊ni⌋<n23时,可以O(1)O(1)得到第二层结果。当⌊ni⌋>n23⌊ni⌋>n23时,使用原先的方法求解,这一部分的时间复杂度是O(∑n13i=1ni−−√)=O(n23)O(∑i=1n13ni)=O(n23)
总时间复杂度 O(n23)O(n23) 1e11大概要跑5s
代码示例
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 lll;
const int mod=1e9+7;
inline lll cal(lll l,lll r)
{
return (l+r)*(r-l+1)/2;
}
inline lll solve(ll up)//solve \sum_{i=1}^{n} up/i *i;
//显然只有i<=up时有贡献
{
// num++;
// if(num%10000==0) cout<<clock()<<endl;
lll res=0;
for(ll l=1,r;l<=up;l=r+1){
r=up/(up/l);
res=(res+up/l*cal(l,r));
}
return res;
}
inline void write(__int128 x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9) write(x/10);
putchar(x%10+'0');
}
//lll help1[maxn];//solve f(n/1) f(n/2) f(n/3) f(n/\sqrt(n))
//lll help2[maxn];//solve 1 2 3 \sqrt{n}
const int maxn=21550000;
ll g[maxn];//n^(2/3) g(n)=\sum_{i|n} i
lll f[maxn];//sum_{i=1}^{n} [n/i]*i
int ans[maxn/10];
int help[maxn];//存每个数最小质因子^指数 如12存2^2 18存2^1 32存2^5
bool valid[maxn];
int tot;
void get_prime(int n)
{
memset(valid,true,sizeof(valid));
tot=0;
g[1]=1;help[1]=1;
for(int i=2;i<=n;++i){
if(valid[i]){
ans[++tot]=i;
g[i]=i+1;
help[i]=i;
}
for(int j=1;j<=tot && ans[j]*i<=n;++j){
valid[ans[j]*i]=false;
if(i%ans[j]==0){
help[i*ans[j]]=help[i]*ans[j];
g[i*ans[j]]=g[i]*ans[j]+g[i/help[i]];
break;
}
else{
help[i*ans[j]]=ans[j];
g[i*ans[j]]=g[i]*g[ans[j]];
}
}
}
}
int main()
{
get_prime(maxn);
f[0]=0;
for(int i=1;i<maxn;++i) f[i]=f[i-1]+g[i];
//cout<<clock()<<endl;
//freopen("in.txt","r",stdin);
int t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
lll ans1=0;
for(ll l=1,r;l<=n;l=r+1){
r=n/(n/l);
ll tp=n/l;
if(tp<maxn) ans1+=f[tp]*cal(l,r);
else ans1+=solve(tp)*cal(l,r);
}
lll ans2=0;//i=j
for(ll i=1;i*i<=n;++i){
ll tp=i*i;
ans2+=n/tp*tp;
}
ans1+=ans2;
assert(ans1%2==0);
ans1/=2;
ans1=((lll)n)*n*(n+1)/2-ans1;
write(ans1);
cout<<endl;
}
return 0;
}