BZOJ3309: DZY Loves Math

42 篇文章 0 订阅

先尝试化一下题目给的式子
ni=1mj=1f(gcd(i,j))>
df(d)n/di=1m/dj=1,gcd(i,j)=11 >
Df(D)dμ(d)n/Ddi=1m/Ddj=1>
Kd|Kf(Kd)μ(d)nKmK
g=fμ>
Kg(K)nKmK
我们将它化成了卷积,但是f不是个积性函数,不能直接筛,所以考虑 μ 的一些性质
首先如果d含平方因子,我们是不用考虑它的
d 不含平方因子时,f(Kd)只可能是K的最大次幂或最大次幂-1,分开考虑
当其是最大次幂-1时,K的最高次幂质因子d全部要含,
设K含m个最高次幂质因子,其他还有k个质因子,
基于 μ 的定义和性质,对答案的贡献是

(f(K)1)×(1)m×i=0kCik×(1)i

ki=0Cik×(1)i 在k≠0时是=0的
于是再分情况,先讨论k≠0的情况
那么当 f(Kd) 为最大次幂-1时,贡献为0
当其不为最大次幂-1时,不好算方案,所以用总方案的 μ -其为最大次幂-1的μ,推理同上,得0-0=0,所以当k≠0时,对答案无贡献
当k=0时,即K的所有质因子指数相同,
当m为奇数时,推理同上,总贡献为 (f(K)1)+f(K)=1
当m为偶数时,推理同上,总贡献为 (f(K)1)f(K)=1
于是我们就得到了一种线性筛 g 的方法,再维护一下g的前缀和 H ,查询的时候就可以O(n)



code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 10000001;

int p[maxn],pr,mpn[maxn],dto[maxn];
int num[maxn];
int H[maxn];
bool v[maxn],jud[maxn];

int prepare()
{
    H[1]=0;
    for(int i=2;i<maxn;i++)
    {
        if(!v[i])
        {
            p[++pr]=i;
            mpn[i]=1; dto[i]=1;
            num[i]=1;
            jud[i]=true;
        }
        if((jud[dto[i]]&&mpn[dto[i]]==mpn[i])||dto[i]==1) jud[i]=true;
        if(jud[i])
        {
            if(num[i]&1) H[i]=1;
            else H[i]=-1;
        }
        for(int j=1;j<=pr&&p[j]*i<maxn;j++)
        {
            int k=p[j]*i;
            v[k]=true;
            if(i%p[j]==0)
            {
                mpn[k]=mpn[i]+1;
                dto[k]=dto[i];
                num[k]=num[i];
                break;
            }
            mpn[k]=1;
            dto[k]=i;
            num[k]=num[i]+1;
        }
        H[i]+=H[i-1];
    }
}

int main()
{
    prepare();
    int t; scanf("%d",&t);
    while(t--)
    {
        ll n,m; scanf("%lld%lld",&n,&m);
        ll l=1,r=min(n,m);
        ll ret=0;
        while(l<=r)
        {
            ll nex=min(n/(n/l),m/(m/l));
            ret += (n/l)*(m/l)*(ll)(H[nex]-H[l-1]);
            l=nex+1;
        }
        printf("%lld\n",ret);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值