杜教筛 与 数论函数(狄雷克卷积)

为了改变数论只会GCD的尴尬局面,我们来开一波数论:

数论函数:

  • 数论函数是定义域在正整数的函数。
  • 积性函数: f(ab)=f(a)f(b),gcd(a,b)=1 ,完全积性函数: f(ab)=f(a)f(b) 。
  • 常见积性函数: φ(n) ,μ(n) (莫比乌斯函数), d(n) (因子个数), σ(n) (因子和)。
  • 单位函数 : e(n)=[n=1] 。
  • 常见完全积性函数: Idk(n)=n^k , 1(n)=Id0(n) , Id(n)=Id1(n) 。

我们 有以下令人窒息的操作:

(F*G)(x)=∑d|n F(d)*G(n/d)

这种操作我们称之为狄雷克卷积。满足交换律,结合律,分配律以及 f∗e=f 。

我们有:      d(n)=(1∗1)(n)

σ(n)=(Id∗1)(n)

e=μ∗1

都可以由积性函数的性质再套一个乘法分配率可以证出来。

我们有两个重要的数论函数 φ(n) ,μ(n) 。

φ(n),1到n里面与N互质的数的个数。有重要的等式:φ*1=id

μ(n), 我们可以理解为1的逆元。 我们发现狄雷克卷积可以构成一个群,生成元是e ,1* μ=e,我们又知道*1又叫求该函数的狄雷克前缀和,那么再卷上μ就是原来的函数了。

我们设F=f*1,则 f=F* μ

那么我们就可以做一些简单的题目了:

bzoj 3884 .这道题我们要一个结论:

X^i=X^(i>φ(p)?i mod  φ(p)+φ(p):i) (mod p)

  特殊的 ,我们有 X^i=X^(i%(p-1)) (mod p) p为质数。

我们还要知道φ(φ(p))<n/2,那么我们不妨迭代指数,最多迭代log p次指数为1,我们停止迭代。

#include<bits/stdc++.h>
using namespace std;
int sed(int &p){
    int ll=0;
    while (p%2==0) ll++,p>>=1; return ll;
}
int fai(int x){
     int i,re=x;
    for(i=2;i*i<=x;i++)
        if(x%i==0)
        {
            re/=i;re*=i-1;
            while(x%i==0)
                x/=i;
        }
    if(x^1) re/=x,re*=x-1;
    return re;
}
long long QP(long long x,int y,int p){
      long long re=1;
    while(y)
    {
        if(y&1) (re*=x)%=p;
        (x*=x)%=p; y>>=1;
    }
    return re;
}
long long dos(int p){
    if (p==1) return 1;
    int k=sed(p), fi=fai(p);
    long long re=dos(fi);
    (re+=fi-k%fi)%=fi;
  re=QP(2,re,p)%p;
  return (re<<k);
}
int main () {
    int t,p;
    scanf("%d",&t);
    while (t--) {
        scanf("%d",&p);
        printf("%lld\n",dos(p)%p);
    }
}

bzoj 2186

这某非就是传说中的gcd?

#include<bits/stdc++.h>
#define inff 10000000
#define N   10000090
int n,m,p,tot,tops,t,T;
int ans[N];int fa[N];
bool f[N];char c; int b,inf;
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x) {
    c=nc();
    b=1;
    for (; !(c>=‘0‘ && c<=‘9‘); c=nc()) if (c==‘-‘) b=-1;
    for (x=0; c>=‘0‘ && c<=‘9‘; x=x*10+c-‘0‘,c=nc());
    x*=b;
}
inline void exgcd(int a,int b,int &x,int &y)
{
    if (!b){x=1;y=0;return;}
    exgcd(b,a%b,x,y);
    int t=x;x=y;y=t-a/b*x;
}
inline int getinv(int a)
{
    int x=0,y=0;
    exgcd(a,p,x,y);
    return (x%p+p)%p;
}
void getfine(){
    ans[1]=1;t=0;
     for (int i=2;i<=inf;i++)
    {
        if (!f[i])
            ans[i]=(long long)1ll*ans[i-1]*(i-1)%p*getinv(i)%p,fa[++t]=i;
        else ans[i]=ans[i-1];
        for(int j=1;j<=t;j++)
        {
            if((long long)fa[j]*i>inf)break;
            f[fa[j]*i]=1;
            if(i%fa[j]==0)break;
        }
    }
}
inline void write(int x)
{
     if(x<0) putchar(‘-‘),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+‘0‘);
}
int main (){
    //scanf("%d%d",&T,&p);
  read(T); read(p);
    inf=min(p,inff);
    getfine();fa[0]=1;
    for (int i=1;i<=inf;i++) fa[i]=(long long)(1ll*fa[i-1]*i)%p;
    while (T--){
        //scanf("%d%d",&n,&m);
        read(n); read(m);
        int top=1ll*fa[n]%p*ans[m]%p;
        //printf("%d\n",top);
        write(top); putchar(‘\n‘);
    }
}

bzoj 2440

二分答案,我们转为一个计数问题,我们统计有多少符合条件的数小于X?

由容斥原理得 ans=∑ x/(i*i)*μ(i)

#include<bits/stdc++.h>
#define N 50000
#define ll long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int T;
ll ans,K;
int tot;
int mu[50005],pri[50005];
bool mark[50005];
void getmu()
{
    mu[1]=1;
    for(int i=2;i<=N;i++)
    {
        if(!mark[i])pri[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot&&pri[j]*i<=N;j++)
        {
            mark[i*pri[j]]=1;
            if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
            else mu[i*pri[j]]=-mu[i];
        }
    }
}
ll cal(int x)
{
    ll sum=0;
    int t=sqrt(x);
    for(int i=1;i<=t;i++)
        sum+=x/(i*i)*mu[i];
    return sum;
}
int main()
{
    getmu();
    T=read();
    while(T--)
    {
        K=read();
        ll l=K,r=1644934081;
        while(l<=r)
        {
            ll mid=(l+r)>>1;
            if(cal(mid)>=K)ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

给出T组N和M,依次求出的值

我们考虑反演:∑i∑j  (gcd=1)   =∑i∑j ∑d |gcd(i,j) μ(i)=∑ d μ(d) (n/d)*(m/d)

我们发现其只有O(N^0.5)个整点,分块处理。

#include<bits/stdc++.h>
#define N 1000000
bool mark[N+5];
int prime[N+5];
int num;
long long sum[N+5];
int euler[N+5];
inline int Min(int a,int b){return a<b?a:b;}
void Euler()
{
    int i,j;
    num = 0;
    memset(euler,0,sizeof(euler));
    memset(prime,0,sizeof(prime));
    memset(mark,0,sizeof(mark));
    euler[1] = 1; // multiply function
    sum[1]=1;
    for(i=2;i<=N;i++)
    {
        if(!mark[i])
        {
            prime[num++] = i;
            euler[i] = -1;
        }
        for(j=0;j<num;j++)
        {
            if(i*prime[j]>N){break;}
            mark[i*prime[j]] = 1;
            if(i%prime[j]==0)
            {
                euler[i*prime[j]] = 0;
                break;
            }
            else
            {
                euler[i*prime[j]] = -euler[i];
            }
        } sum[i]=sum[i-1]+euler[i];
    }
}

int main()
{
    int i;
    int M1,M2;
    Euler(); int t;
    scanf("%d",&t);
   while (t--){
    scanf("%d%d",&M1,&M2);
    long long ans = 0;
    int min = Min(M1,M2);
    /*for(i=1;i<=min;i++)
    {
        sum += euler[i]*(M1/i)*(M2/i);
    }*/
    for (int i=1,last;i<=min;i=last+1){
        last=Min(M1/(M1/i),M2/(M2/i));
        ans+=(sum[last]-sum[i-1])*(M1/i)*(M2/i);
        }
    printf("%lld\n",ans);
        }
}

给出T组N和M,求的值

我们考虑反演:∑i∑j  (gcd=1)   =∑i∑j ∑d |gcd(i,j)φ (i)=∑ dφ(d) (n/d)*(m/d)

我们发现其只有O(N^0.5)个整点,分块处理。

#include<bits/stdc++.h>
#define N 1000000
bool mark[N+5];
int prime[N+5];
int num;
long long sum[N+5];
int euler[N+5];
inline int Min(int a,int b){return a<b?a:b;}
void Euler()
{
    int i,j;
    num = 0;
    memset(euler,0,sizeof(euler));
    memset(prime,0,sizeof(prime));
    memset(mark,0,sizeof(mark));
    euler[1] = 1; // multiply function
    sum[1]=1;
    for(i=2;i<=N;i++)
    {
        if(!mark[i])
        {
            prime[num++] = i;
            euler[i] = i-1;
        }
        for(j=0;j<num;j++)
        {
            if(i*prime[j]>N){break;}
            mark[i*prime[j]] = 1;
            if(i%prime[j]==0)
            {
                euler[i*prime[j]] = euler[i]*prime[j];
                break;
            }
            else
            {
                euler[i*prime[j]] = euler[i]*euler[prime[j]];
            }
        } sum[i]=sum[i-1]+euler[i];
    }
}

int main()
{
    int i;
    int M1,M2;
    Euler(); int t;
    scanf("%d",&t);
   while (t--){
    scanf("%d%d",&M1,&M2);
    long long ans = 0;
    int min = Min(M1,M2);
    /*for(i=1;i<=min;i++)
    {
        sum += euler[i]*(M1/i)*(M2/i);
    }*/
    for (int i=1,last;i<=min;i=last+1){
        last=Min(M1/(M1/i),M2/(M2/i));
        ans+=(long long)1ll*(sum[last]-sum[i-1])*(M1/i)*(M2/i);
        }
    printf("%lld\n",ans);
        }
}

那么我们就可以上杜教筛了。

举个例子:这里  我们要在线性以下的时间搞定φ的前缀和。

(⊙o⊙)… 我放弃了,不会打数学公式。

丢个链接吧   这里   ,  这里

#include<bits/stdc++.h>
#define N 4011007
#define LL long long
#define mo 1000000007
#define fg
#define int LL
using namespace std;
LL pim[N>>3],fi[N],tot,a[N],b[N],inv2=500000004,s=N-1007,n,SI,fis[N];
inline LL Mo(LL x){x%=mo;if (x<0) x+=mo;return x;}
LL tr(LL x) {if (x<=s) return a[x]; return b[n/x];}
void put(LL x,LL y) {if (x<=s) a[x]=y; else b[n/x]=y;}
LL F(LL x) {
    if (x<SI) return fis[x];
    if (tr(x)) return tr(x);
    LL ans=Mo(x)*Mo(x+1)%mo*inv2%mo;
    for (LL i=2,last;i<=x;i=last+1) {
        last=x/(x/i);
        ans=Mo(ans-F(x/i)*Mo(last-i+1)%mo);
    } put(x,ans);return ans;
}
void Pin(int MA){
    fis[1]=1;
    for (int i=2;i<MA;i++) {
        if (!fi[i]) pim[++tot]=i,fi[i]=i-1;
        for (int j=1;j<=tot&&pim[j]*i<MA;j++)
         if(i%pim[j]) fi[i*pim[j]]=fi[i]*(pim[j]-1);
         else {fi[i*pim[j]]=fi[i]*pim[j]; break;}
        fis[i]=Mo(fis[i-1]+fi[i]);
    }
}
void Init() {
    SI=min((int)powl(n,0.666),(LL)N-12);
    Pin(SI);
}
signed main () {
    scanf("%lld",&n);
    Init();
    printf("%lld",F(n));
}

再丢几道题目:

莫比乌斯前缀

#include<bits/stdc++.h>
#define LL long long
#define N 4000007
using namespace std;
map<LL,int> ma;
LL a,n;
int LS,fis[N],pim[N>>3],tot,fi[N],u[N];
bool p[N];
LL F(LL x){
    if (x<LS) return fis[x];
    if (ma.count(x)) return ma[x];
    LL ans=1;
    for (LL i=2,last;i<=x;i=last+1) {
        last=x/(x/i); ans=ans-(last-i+1)*F(x/i);
    } ma[x]=ans; return ans;
}
void LST(int LS){
    fis[1]=1;
    for (int i=2;i<LS;i++) {
        if (!p[i]) u[i]=-1,pim[++tot]=i;
        for (int j=1;j<=tot&&i*pim[j]<LS;j++) {
            p[i*pim[j]]=1; if (i%pim[j]) u[i*pim[j]]=-u[i]; else{ u[i*pim[j]]=0; break;}
        }
        fis[i]=(fis[i-1]+u[i]);
    }
}
void init() {
    LS=min((int)pow(n,0.6667),N-47);
    LST(LS);
}
int main () {
    scanf("%lld %lld",&a,&n);
    init();
    printf("%lld",F(n)-F(a-1));
}

公约数前缀和

#include<bits/stdc++.h>
#define mo 1000000007
#define N 5000007
#define LL long long
using namespace std;
LL n,fis[N],pim[N>>3],fi[N],IS,inv2=500000004,tot,ans,a[N],b[N],s=N-1007;
inline LL Mo(LL x){x%=mo; if (x<0) x+=mo; return x;}
//map<LL,LL> Ma;
LL tr(LL x) {if (x<=s) return a[x]; return b[n/x];}
void put(LL x,LL y) {if (x<=s) a[x]=y; else b[n/x]=y;}
LL fiz(LL x){
    if (x<=IS) return fis[x];
    if (tr(x)) return tr(x);
//    printf("%d\n",x);
    LL ans=x%mo*((x+1)%mo)%mo*inv2%mo;
    for (LL i=2,last;i<=x;i=last+1) {
        last=x/(x/i);
        ans=(ans-fiz(x/i)*
        ((last-i+1)%mo)%mo)%mo;
    }
    put(x,ans); return ans;
}
void P_pit(LL ma_l){
    fis[1]=1;
    for (int i=2;i<=ma_l;i++) {
        if (!fi[i]) pim[++tot]=i,fi[i]=i-1;
        for (int j=1;j<=tot&&i*pim[j]<=ma_l;j++)
            if (i%pim[j]) fi[i*pim[j]]=fi[i]*(pim[j]-1);
            else {fi[i*pim[j]]=fi[i]*pim[j];break;}
        fis[i]=fi[i]+fis[i-1];
    }
}
void init() {
    IS=min(N-17,(int)powl(n,0.6666));
    P_pit(IS);
    fiz(n);
}
int main () {
//    freopen("a.in","r",stdin);
    scanf("%lld",&n);
    init();
    for (LL i=1,last;i<=n;i=last+1) {
        last=n/(n/i);
        ans=(ans+Mo(n/i)*Mo(n/i)*Mo(fiz(last)-fiz(i-1))%mo);
        ans=Mo(ans);
    }
    printf("%lld\n",ans); return 0;
}

来一发技巧。对于单点杜教筛的话,我们可以这样存取值

LL tr(LL x) {if (x<=s) return a[x]; return b[n/x];}
void put(LL x,LL y) {if (x<=s) a[x]=y; else b[n/x]=y;}

那么我们就不用map 或哈希了。

最后安利一发 任一洲 dalao。

原文地址:https://www.cnblogs.com/rrsb/p/8321489.html

时间: 2024-10-09 23:34:27

杜教筛 与 数论函数(狄雷克卷积)的相关文章

卷积 &amp; 杜教筛

卷积 卷积定义: 如果有数论函数\(f, g\), 那么它们卷积的第\(n\)项为\((f * g) (n)\),设这个卷出来的新函数为h,那么有 \[h(n) = \sum_{k | n} f(k) g(n / k) = \sum_{ij = n}f(i) g(j)\] 一些性质: 1,积性函数的卷积还是积性函数 证明: 现有\(f, g\)为积性函数,且\(gcd(p, q) == 1\),求证\(h(p) \cdot h(q) = h(qp).\) \[h(p) \cdot h(q) =

【数论】狄利克雷卷积及其快速计算方法及杜教筛

目录(假的 狄利克雷卷积基础知识 数论函数 狄利克雷卷积定义 狄利克雷卷积性质 常用卷积 卷积计算方法 最暴力的暴力 稍好的暴力 优美的暴力 莫比乌斯反演(待填坑) 杜教筛 经典杜教筛 第二种杜教筛 第三种杜教筛 背景 本人即将去CTS&APIO2019,由于一些特殊原因,发现自己数论突然变得很菜. 就决定在去的前一天,翻出来以前的数论学习资料看一看.翻到了czgj的校内狄利克雷卷积课件,发现其中提到了的任意数列\(f(n)\)和\(g(n)\)的狄利克雷卷积\((f*g)(n)\)(从1到n,

数论入门——莫比乌斯函数,欧拉函数,狄利克雷卷积,线性筛,莫比乌斯反演,杜教筛

一个菜鸡对数论的一点点理解... 莫比乌斯函数 定义函数\(\mu(n)\)为: 当n有平方因子时,\(\mu(n)=0\). 当n没有平方因子时,\(\mu(n)=(-1)^{\omega(n)}\),\(\omega(n)\)表示n不同质因子的个数. 性质1: \(\sum_{d|n}\mu(d)=[n=1]\) 证明:我们把n分解质因数,则原式\(=(-1+1)^{\omega(n)}=0\). 因为对于不同的质因子,只有选和不选两种方案,这是一个组合数相加的形式,偶数加奇数减,根据二项式

[日常摸鱼]杜教筛

因为博主比较菜所以可能一些地方写的有问题或者不清楚,以及我的废话有点多- 在这里先感谢下小伙伴ww @MoebiusMeow 的帮助~ 参考资料: [1]浅谈一类积性函数的前缀和(skywalkert) [2]杜教筛--省选前的学习1(_rqy) (下面约定$[p]$表示满足条件$p$时为1不满足为0,以及下面说的函数都是数论函数) 前置技能(一些定义) 数论函数:若$f:Z^{+} \rightarrow C$,则称$f$为数论函数 积性函数:若一个数论函数$f(n)$对于所有$m_1 \bo

51nod 1220 约数之和(杜教筛 + 推推推推推公式)

题意 给出\(n(1\leq n \leq 10^9)\),求\(\sum_{i=1}^n\sum_{j=1}^n\sigma(ij)\),其中\(\sigma(n)\)表示\(n\)的约数之和. balabala 交了两道杜教筛的的板子题(51nod 1239, 1244)就看到了这题,然后不会搞,然后看题解看了一天一夜终于彻底搞明白一发A掉了...感觉学到了很多,写个博客整理一下,如有错请指出. 技能需求 数论函数与线性筛 莫比乌斯反演(也可以当成容斥去理解) 狄利克雷卷积 杜教筛 强大的数

杜教筛之逆运算

前言: 下面介绍的这种方法并不常见,但是非常的有用 准确来说,我是来拓荒的,所以有什么问题请一定指出 前置技能: 积性函数 狄利克雷卷积 一定式子转化能力 其实对杜教筛知识点方面要求并不是很高 简单介绍几种常用积性函数: \(1.\text{欧拉函数:}\phi(x)=\text{ 1-x与x互质的数的个数}\) \(2.\text{莫比乌斯函数:}\mu(x)\text{ 这个函数定义难以描述,可以自行查找}\) \(3.\text{约数个数:}d(x)=\sum_{d|x}1\) \(4.\

杜教筛 学习总结

看了看唐老师的blog,照猫画虎的做了几道题目,感觉对杜教筛有些感觉了 但是稍微有一点难度的题目还是做不出来,放假的时候争取都A掉(挖坑ing) 这篇文章以后等我A掉那些题目之后再UPD上去就好啦 由于懒得去写怎么用编辑器写公式,所以公式就准备直接copy唐老师的啦 首先积性函数和完全积性函数什么的就不再多说了 列举常见的积性函数: 1.约数个数函数和约数个数和函数 2.欧拉函数phi 3.莫比乌斯函数mu 4.元函数e 其中e(n)=[n==1] 5.恒等函数I 其中I(n)=1 6.单位函数

杜教筛进阶+洲阁筛讲解+SPOJ divcnt3

Part 1:杜教筛进阶在了解了杜教筛基本应用,如$\sum_{i=1}^n\varphi(i)$的求法后,我们看一些杜教筛较难的应用.求$\sum_{i=1}^n\varphi(i)*i$考虑把它与$id$函数狄利克雷卷积后的前缀和.$$\sum_{i=1}^n\sum_{d|i}\varphi(d)*d*\frac id=\sum_{i=1}^ni^2$$枚举$T=\frac id$,原式化为$$\sum_{T=1}^nT\sum_{d=1}^{\lfloor\frac nT\rfloor}

【Luogu3768】简单的数学题(莫比乌斯反演,杜教筛)

[Luogu3768]简单的数学题(莫比乌斯反演,杜教筛) 题面 洛谷 \[求\sum_{i=1}^n\sum_{j=1}^nijgcd(i,j)\] $ n<=10^9$ 题解 很明显的把\(gcd\)提出来 \[\sum_{d=1}^nd\sum_{i=1}^n\sum_{j=1}^nij[gcd(i,j)==d]\] 习惯性的提出来 \[\sum_{d=1}^nd^3\sum_{i=1}^{n/d}\sum_{j=1}^{n/d}ij[gcd(i,j)==1]\] 后面这玩意很明显的来一发