【JZOJ 3623】【SDOI2014】数表

Description

这里写图片描述

Solution

本题为看到gcd上反演系列,
fd 表示能整除d的自然数总和,

Ans=i=1nj=1mfgcd(i,j)

反演的套路就不写了,
Ans=T=1min(n,m)nTmTd|Tfdμ(Td)

有因为有f的大小限制,这个只能得部分分,
正解是离线,按f的限制排序,
再把f排序,
sn=d|nμ(nd)fd[fd<=a]
每次限制变大,就会有一些新的f符合要求,
fi 现在变得符合要求,
那么,影响到的s就只有是i的倍数的s,
就是 sd=sd+fi(d|i)
这个不停枚举d的过程是 O(nlog(n))
有因为要分块,要做s的前缀和,
所以就用树状数组。

复杂度: O(nlog(n)2+Q(n+m)log(n))

PS:祝君卡常愉快!
分块的时候注意尽量不要用(LL)/(LL),超级慢。

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define min(q,w) ((q)<(w)?(q):(w))
#define NX(q) ((q)&(-(q)))
using namespace std;
typedef long long LL;
const int N=200500;
LL mo=1073741824;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n;
int f[N];
LL Ans[N];
bool prz[N];
int pr[N],mu[N];
struct qqww
{
    LL v;int n;
}b[N];
struct qwqw
{
    int n,m,K,i;
}sc[N];
int c[N],g[N];
bool cz[N];
LL sum[N];
void ss(int I,int q,LL e)
{
    f[q]=e;
    fo(i,I,pr[0])
    {
        LL t=1;
        LL t1=e;
        bool z=1;
        while(1)
        {
            t*=pr[i];
            t1=(t1+e*t);
            if(t*q>n)break;
            if(t1>1e9){printf("swddwdw %lld\n",t*q); break;}
            ss(i+1,t*q,t1);
            z=0;
        }
        if(z)break;
    }
}
bool PX(qqww q,qqww w){return q.v<w.v;}
bool PXsc(qwqw q,qwqw w){return q.K<w.K;}
void add(int q,LL w){for(;q<=n;q+=NX(q))f[q]+=w;}
LL Gsum(int q)
{
    // return 1;
    LL ans=0;
    for(;q>0;q-=NX(q))ans+=f[q];
    return ans;
}
LL fk(int n,int m)
{
    int i=1;
    LL ans=0;
    while(i<=n)
    {
        LL nx=min(n/(n/i),m/(m/i));
        ans=(ans+(LL)(n/i)*((LL)m/i)*(Gsum(nx)-Gsum(i-1)))%mo;
        i=nx+1;
    }
    return ans;
}
int main()
{
    freopen("table.in","r",stdin);
    freopen("table.out","w",stdout);
    int q,w;
    mo*=2;
    n=1e5+10; 
    mu[1]=1;
    fo(i,2,n)
    {
        if(!prz[i])pr[++pr[0]]=i,mu[i]=-1;
        fo(j,1,pr[0])
        {
            LL t=pr[j]*i;
            if(t>n)break;
            prz[t]=1;
            if(i%pr[j]==0)break;
            mu[t]=-mu[i];
        }
    }
    ss(1,1,1);
    f[n+1]=2e9;
    fo(i,1,n+1)b[i].v=f[i],b[i].n=i,f[i]=0;
    sort(b+1,b+1+n,PX);
    int _;
    read(_);
    fo(i,1,_)
    {
        read(sc[i].n),read(sc[i].m),read(sc[i].K),sc[i].i=i;
        if(sc[i].n>sc[i].m)swap(sc[i].n,sc[i].m);
    }
    sort(sc+1,sc+1+_,PXsc);
    q=1;
    fo(i,1,_)
    {
        while(b[q].v<=sc[i].K)
        {
            int t=b[q].n,cs=b[q].v;
            int jn=n/t;
            fo(j,1,jn)if(mu[j])add(j*t,cs*mu[j]);
            q++;
        }
        Ans[sc[i].i]=(fk(sc[i].n,sc[i].m)+mo)%mo;
    }
    fo(i,1,_)printf("%lld\n",Ans[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值