2016江苏省赛H题(莫比乌斯反演+分块求和)

题目描述

就是让你计算这个东西

i=1nj=1mi2j2gcd(i,j)n,m1e6

分析

和经典的这个公式很像 ni=1mj=1gcd(i,j) 只需做一些简单的转化.

i=1nj=1mi2j2gcd(i,j)=i=1nj=1mi2j2dgcd(i,j)ϕ(d)=i=1nj=1mi2j2di,djϕ(d)=d=1nϕ(d)dii2djj2       (3)=d=1nϕ(d)k=1n/d(kd)2k=1m/d(kd)2=d=1nϕ(d)d4k=1n/dk2k=1m/dk2=d=1nϕ(d)d4n/d(n/d+1)(2(n/d)+1)6m/d(m/d+1)(2(m/d)+1)6

比赛的时候只想到了第三步以为 O(nlgn) 的复杂度已经够了哪知道交上去各种T,回去睡了一晚上之后就发现了平方和用公式计算这样就简单许多了.然后由于它是关于 n/d 的公式显然可以用分块求和将复杂度降低到 O(n+m)

AC code


#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include<cmath>
#include <cstring>
#include <map>
#include <set>
#include <iomanip>
#include <bitset>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int MOD = 1e9+7;
const int MAX_P = 2e4+10;
const int maxn =1e6+10;
const int MAX_V = 5e5+10;
const int maxv = 1e6+10;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;

int n,m;

LL phi[maxn],prime[maxn],cnt;
void phi_table() {
    memset(prime,0,sizeof(prime));
    cnt =0;
    phi[1] = 1;
    for(int i=2 ; i<maxn ; ++i)
    {
        if(!prime[i]){
            prime[cnt++] = i;
            phi[i] = i-1;
        }
        for(int j =0 ; j< cnt && i*prime[j] < maxn ; ++j){
            prime[i*prime[j]] = 1;
            if(i % prime[j])phi[i*prime[j]] = phi[i]*(prime[j]-1);
            else{
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
        }
    }
}
LL f1[maxn],f2[maxn];

LL sum[maxn];

int main() {
    phi_table();
    // for(int d = 1 ; d<= 100 ; ++d)
    // std::cout << phi[d] << '\n';
    int T;
    int kase =0;
    scanf("%d",&T );
    sum[0] = 0;
    for(LL i=1 ; i<maxn ; ++i){
        sum[i] = sum[i-1]+(phi[i]*i*i %MOD )*(i*i%MOD)%MOD;
        sum[i]%=MOD;
    }
    while (T--) {


        scanf("%d%d",&n,&m );
        if(n>m)swap(n,m);
        LL ans =0;
        for(LL d =1 ,last =1 ; d<=n ; d = last+1){
            LL a = (LL)(n/d)*(n/d+1)/2*(2*(n/d)+1)/3 % MOD;
            LL b = (LL)(m/d)*(m/d+1)/2*(2*(m/d)+1)/3 % MOD;
            last = min(n/(n/d),m/(m/d));
            ans+=((sum[last]-sum[d-1]+MOD)%MOD) *(a*b % MOD) % MOD;
            ans%=MOD;
        }
        printf("Case #%d: %d\n",++kase,(int)ans );
    }

    #ifdef LOCAL_DEFINE
        cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
    #endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值