杜教筛

一定要先%mmh学长
例①:
P4213 【模板】杜教筛(Sum):

题目描述
给定一个正整数N(N≤231−1)

ans1= ∑φ(i) from 1 to n

ans2= ∑μ(i) from 1 to n

输入格式
一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N,代表一组询问

输出格式
一共T行,每行两个用空格分隔的数ans1,ans2

输入输出样例
输入 #1 复制
6
1
2
8
13
30
2333
输出 #1 复制
1 1
2 0
22 -2
58 -3
278 -3
1655470 2

有点卡时,要把不需要ll的全部改成int。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#define ll long long
#define llu unsigned ll
#define iu unsigned int
using namespace std;
const int maxn=6000100;
int prime[maxn],cnt;
bool ha[maxn];
int mu[maxn];
ll p[maxn];
unordered_map<int,ll>ansp;
unordered_map<int,int>ansmu;


void Prime(void)
{
    ha[1]=true;
    mu[1]=p[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!ha[i])
        {
            prime[cnt++]=i;
            mu[i]=-1;
            p[i]=i-1;
        }
        for(int j=0;j<cnt&&prime[j]*i<maxn;j++)
        {
            ha[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                p[i*prime[j]]=p[i]*prime[j];
                break;
            }
            mu[i*prime[j]]=-mu[i];
            p[i*prime[j]]=p[i]*p[prime[j]];
        }
    }
    for(int i=2;i<maxn;i++)
        mu[i]+=mu[i-1],p[i]+=p[i-1];
}

int sum_mu(int n)
{
    if(n<maxn) return mu[n];
    if(ansmu[n]) return ansmu[n];
    int ans=0;
    for(iu l=2,r;l<=n;l=r+1)//n为最值的时候,r+1可能爆int
    {
        r=n/(n/l);
        ans+=(r-l+1)*sum_mu(n/l);
    }
    ansmu[n]=1-ans;
    return ansmu[n];
}

ll sum_p(int n)
{
    if(n<maxn) return p[n];
    if(ansp[n]) return ansp[n];
    ll ans=0;
    for(iu l=2,r;l<=n;l=r+1) //n为最值的时候,r+1可能爆int
    {
        r=n/(n/l);
        ans+=1ll*(r-l+1)*sum_p(n/l);
    }
    ansp[n]=1ll*n*(1ll*n+1)/2ll-ans;//这里爆不了ll,但是n为最值的时候,n+1会爆int
    return ansp[n];
}

signed main(void)
{
    Prime();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        printf("%lld %d\n",sum_p(n),sum_mu(n));
    }
    return 0;
}

例②: HDU - 6706 :huntian oy :
One day, Master oy created a new function to celebrate his becoming a ‘huntian’ in majsoul.

f(n,a,b)=∑(from 1 to n)∑(from 1 to i) gcd(ia−ja,ib−jb)[gcd(i,j)=1]%(109+7)

Given n, a and b, Master oy wanted Newbie jj who was still a ‘chuxin’ to answer the value of f(n,a,b).
Input
There are multiple test cases.

The first line contains an integer T, indicating the number of test cases.

For each test case, there are three positive integers n, a and b which are separated by spaces. It’s guaranteed that a and b are coprime.

1≤n,a,b≤1e9

T=1e4, but there are only 10 test cases that n is over 1e6.
Output
For each test case, an integer in one line representing your answer.
Sample Input
2
1 2 3
100 2 3
Sample Output
0
101542

题解:就是求 ∑(from 1 to n) i*φ(i)。
当时公式化出来了,也知道是杜教筛,但是不会杜教筛。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<map>
#include<unordered_map>
#define ll long long
#define llu unsigned ll
#define iu unsigned int
using namespace std;
const int maxn=3000100;
const int mod=1e9+7;
ll prime[maxn],p[maxn],cnt;
ll inv2,inv6;
bool ha[maxn];
unordered_map<ll,ll>ansp;

ll mypow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

void Prime(void)
{
    ha[1]=true;
    p[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!ha[i])
        {
            prime[cnt++]=i;
            p[i]=i-1;
        }
        for(int j=0;j<cnt&&prime[j]*i<maxn;j++)
        {
            ha[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                p[i*prime[j]]=p[i]*prime[j];
                break;
            }
            p[i*prime[j]]=p[i]*p[prime[j]];
        }
    }
    for(int i=2;i<maxn;i++)
        p[i]=(p[i]*i+p[i-1])%mod;
}


ll sum(ll n)
{
    if(n<maxn) return p[n];
    if(ansp[n]) return ansp[n];
    ll ans=(n)*(n+1)%mod*(n*2%mod+1)%mod*inv6%mod;
    for(ll l=2,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        ans-=(r-l+1)*(l+r)%mod*inv2%mod*sum(n/l)%mod;
        ans=(ans%mod+mod)%mod;
    }
    ansp[n]=ans;
    return ansp[n];
}

signed main(void)
{
    Prime();
    inv2=mypow(2,mod-2);
    inv6=mypow(6,mod-2);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll n,a,b;
        scanf("%lld%lld%lld",&n,&a,&b);
        printf("%lld\n",(sum(n)-1+mod)%mod*inv2%mod);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值