【BZOJ3529】数表,莫比乌斯反演+BIT

传送门
思路:
复习一下莫比乌斯反演
好像是数论入门题,然后我做了近一天……
式子是这么化没错
nm

i=1nj=1m[σ(gcd(i,j))a]σ(gcd(i,j))

=i=1nj=1md=1n[gcd(i,j)=d][σ(d)a]σ(d)

i=di,j=dj
=d=1n[σ(d)a]σ(d)i=1ndj=1md[gcd(i,j)=1]

=d=1n[σ(d)a]σ(d)k=1ndμ(k)nkdmkd

一开始我化到这里就停下开始求解了
写了一发离线+BIT交上去发现比暴力多跑了一个点(?)
分析了一下复杂度好像是 O(Tn)
被reflash婊了一通
悲伤过后重新来搞,在reflash的提(jiao)示(yu)下化成了下面这样
P=kd
=d=1n[σ(d)a]σ(d)P=1n[d|P]μ(Pd)nPmP

=P=1nnPmPd=1P[d|P][σ(d)a]σ(d)μ(Pd)

F(n)=ni=1[i|n][σ(i)a]σ(i)μ(ni)
=P=1nnPmPF(P)

用BIT维护 F 的前缀和,然后处理每个询问的时候O(n)前缀和搞就可以了
一开始把所有的 σ(i)μ(ni) 保存下来,大约有 106 个,按照 σ(i) 排序
在BZ上跑了10s……rank倒数第5
又被reflash婊了一通
悲伤过后重新来搞,在reflash的提(jiao)示(yu)下开始现求 μ 里的东西(就是枚举d的倍数……)
然后跑了7s……
其实还可以线筛 σ ,因为这是个积性函数(要记录最小质因子的若干次方)
改完以后终于跑到了4s左右……
不过还是很慢
复杂度 O(nlog2n+Tnlogn)
写一下这篇博客,记住自己菜到家的数论知识,顺便%%%reflash

#include<cstdio>
#include<iostream>
#include<algorithm>
#define LL long long
#define mo 2147483647
#define N 100000
using namespace std;
int cnt,prime[N+5],mu[N+5],t[N+5];
LL d[N+5],ans[N+5];
typedef pair<int,int>xy;
xy sigma[N+5];
bool vis[N+5];
struct node{
    int id,n,m,A;
    bool operator <(const node other)const
    {
        return A<other.A;
    }
}q[20005];
void add(int x,int val)
{
    for (;x<=N;x+=x&-x) d[x]+=val;
}
LL get(int x)
{
    LL t=0;
    for (;x;x-=x&-x) t+=d[x];
    return t;
}
main()
{
    mu[1]=1;
    sigma[1]=make_pair(1,1);
    t[1]=1;
    for (int i=2;i<=N;++i)
    {
        if (!vis[i])
            prime[++prime[0]]=i,
            t[i]=i,
            sigma[i]=make_pair(i+1,i),
            mu[i]=-1;
        for (int j=1;j<=prime[0];++j)
        {
            if (prime[j]*i>N) break;
            vis[prime[j]*i]=1;
            if (i%prime[j])
                t[i*prime[j]]=prime[j],
                sigma[i*prime[j]]=make_pair(sigma[prime[j]].first*sigma[i].first,i*prime[j]),
                mu[i*prime[j]]=-mu[i];
            else
            {
                t[i*prime[j]]=t[i]*prime[j];
                sigma[i*prime[j]]=make_pair(sigma[i].first+sigma[i/t[i]].first*t[i]*prime[j],i*prime[j]);
                mu[i*prime[j]]=0;
                break;
            }
        }
    }
    sort(sigma+1,sigma+N+1);
    int T;
    scanf("%d",&T);
    for (int i=1;i<=T;++i)
    {
        scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].A);
        if (q[i].n>q[i].m) swap(q[i].n,q[i].m);
        q[i].id=i;
    }
    sort(q+1,q+T+1);
    int n,m,last=1;
    for (int i=1;i<=T;++i)
    {
        n=q[i].n;m=q[i].m;
        for (;last<=N;++last)
            if (sigma[last].first<=q[i].A)
            {
                int fi=sigma[last].first,se=sigma[last].second;
                for (int j=1;se*j<=N;++j)
                    add(j*se,fi*mu[j]);
            }
            else
                break;
        for (int last,j=1;j<=n;j=last+1)
            last=min(n/(n/j),m/(m/j)),
            ans[q[i].id]+=1LL*(n/j)*(m/j)*(get(last)-get(j-1));
    }
    for (int i=1;i<=T;++i) printf("%d\n",ans[i]&mo); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值