18四川省赛G题 分段 数论函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Feynman1999/article/details/82499962

problem

计算

a n s = i = 1 n j = 1 i ( n   m o d ( i × j ) )

1 n 10 11

思路

解法一

首先写作 i = 1 n j = 1 i ( n n i j i j ) = i = 1 n j = 1 i n i = 1 n j = 1 i n i j i j

对于 i = 1 n j = 1 i n 不用说了,对于 i = 1 n j = 1 i n i j i j = i = 1 n i j = 1 i n i j j

对于第二维,所求是j∈ [ 1 , i ] 我们这里求 [ 1 , n ] 由对称性可知 [ 1 , i ] 的两倍减去 [ i = j ] 的情况即为所求

所以现在目标是 i = 1 n i j = 1 n n i j j

由于 n i 只有 O ( n ) 种取值,我们枚举 n i 的值,对应一段 i 区间,可知对于这一段 i ,第二维值是相同的。

对于第二维的计算类似,也是分块思想。所以写一个solve函数用于求解 f ( n ) = i = 1 n n i i 即可。

时间复杂度 O ( i = 1 n n i ) = O ( n 3 4 ) (只有 O ( n ) 种取值) 注: i = 1 n x n i 的计算式: n ( 1 2 + 1 2 x )

TLE 1e11大概要跑37s

解法二

定义 g ( n ) = f ( n ) f ( n 1 ) = i = 1 n i ( n i n 1 i ) = d | n d

对于 g ( n ) ,即一个数的因子和,我们可以 O ( n ) 求解出其前 n 项,对于本题 n   1 e 11 ,我们处理出 g 的前 n 2 3 项 ,然后求个前缀和就得到了 f ( n ) ,这一部分的时间复杂度是 O ( n 2 3 )

所以对于 i = 1 n i j = 1 n n i j j ,当 n i < n 2 3 时,可以 O ( 1 ) 得到第二层结果。当 n i > n 2 3 时,使用原先的方法求解,这一部分的时间复杂度是 O ( i = 1 n 1 3 n i ) = O ( n 2 3 )

总时间复杂度 O ( n 2 3 ) 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;
}

猜你喜欢

转载自blog.csdn.net/Feynman1999/article/details/82499962