bzoj 4635: 数论小测验 数论+容斥原理

题意

有一个长度为N的数组Ai,每个元素可以取1~M中的一个正整数。那么一共有M^N种可能的数组。因为 SHUXK 对数
论有特殊的爱好,所以他立刻想到了下面两个问题:
1. 对于给定的正整数K,有多少个数组Ai满足GCD(A1,A2…An) = K
2. 对于给定的正整数K,有多少个数组Ai满足K|Lcm(A1,A2…An)
(我相信机智的你在看到这道题的英文名称时就已经猜得八九不离十了)当然,对于 SHUXK 来说,只询问一个K的
情况很简单。于是他加强了一下:给定一个范围L~R,对L~R中所有的正整数K都求出答案,并且把它们加起来作为
最终的结果。然后, SHUXK 就不会了……他需要你来帮助他计算出答案。 由于答案可能很大,我们只要求输出答
案对1,000,000,007取模的结果。
( Type = 1时只询问 gcd 的问题, Type = 2时只询问 lcm 的问题)。
T<=500,Type=1时,M<=10^7,Type=2时M<=1000

分析

第一问:
f(i) f ( i ) 表示 [1,i] [ 1 , i ] 中的数选出n个来 gcd=1 g c d = 1 的方案数。
那么

ans=d=lrf(md) a n s = ∑ d = l r f ( ⌊ m d ⌋ )

f(i) f ( i ) 可以通过容斥来求
f(i)=inj=2if(j) f ( i ) = i n − ∑ j = 2 i f ( j )

不难发现只有 O(n) O ( n ) f(x) f ( x ) 是有用的,所以询问一次的复杂度是 O(mlogn) O ( m l o g n )

第二问:
因为m很小,所以我们可以枚举每个k然后分别求方案。
先把k分解质因数,不难发现最多只有4种质因数。
那么我们可以通过容斥来求有多少种方案使得对于每一种质因子至少有一个数满足其对应指数不小于该质因子的指数。
具体来说就是dfs出所有质因子的选择方案,然后求出 [1,m] [ 1 , m ] 有多少个数满足每一个数对应质因子的指数都小于选出来的质因子指数。
这个也可以通过容斥来求。
具体来说就是有若干个限制,每个限制形如每个数中p的指数不能大于x。
若有一个限制的话数量就是 mmpx m − ⌊ m p x ⌋
若有两个限制的话数量就是 mmpxmqy+mpxqy m − ⌊ m p x ⌋ − ⌊ m q y ⌋ + ⌊ m p x q y ⌋
如此类推。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

const int MOD=1000000007;

int n,m,l,r,stack[100005],top,a[10000005],ans,tot,prime[1005],low[1005],T,ty,s1[105],s2[105],t1,t2,sum;
bool not_prime[1005];

void mod(int &x) {x-=x>=MOD?MOD:0;}

int ksm(int x,int y)
{
    int ans=1;
    while (y)
    {
        if (y&1) ans=(LL)ans*x%MOD;
        x=(LL)x*x%MOD;y>>=1;
    }
    return ans;
}

void solve1()
{
    while (T--)
    {
        scanf("%d%d%d%d",&n,&m,&l,&r);
        top=0;
        for (int i=1,last;i<=m;i=last+1) last=m/(m/i),stack[++top]=m/i;
        a[1]=1;top--;
        while (top)
        {
            int x=stack[top];top--;
            a[x]=ksm(x,n);
            for (int i=2,last;i<=x;i=last+1)
            {
                last=x/(x/i);
                mod(a[x]+=MOD-(LL)a[x/i]*(last-i+1)%MOD);
            }
        }
        int ans=0;
        for (int i=l,last;i<=r;i=last+1)
        {
            last=min(r,m/(m/i));
            mod(ans+=(LL)a[m/i]*(last-i+1)%MOD);
        }
        printf("%d\n",ans);
    }
}

void dg(int x,int y,int d)
{
    if (x>t2) {sum+=(y&1)?-m/d:m/d;return;}
    dg(x+1,y,d);
    dg(x+1,y+1,d*s2[x]);
}

void dfs(int x,int y)
{
    if (x>t1)
    {
        sum=0;dg(1,0,1);
        if (y&1) mod(ans+=MOD-ksm(sum,n));
        else mod(ans+=ksm(sum,n));
        return;
    }
    dfs(x+1,y);
    s2[++t2]=s1[x];dfs(x+1,y+1);t2--;
}

void solve2()
{
    for (int i=2;i<=1000;i++)
    {
        if (!not_prime[i]) prime[++tot]=i,low[i]=i;
        for (int j=1;j<=tot&&i*prime[j]<=1000;j++)
        {
            not_prime[i*prime[j]]=1;
            low[i*prime[j]]=prime[j];
            if (i%prime[j]==0) break;
        }
    }
    while (T--)
    {
        scanf("%d%d%d%d",&n,&m,&l,&r);
        ans=0;
        for (int i=l;i<=r;i++)
        {
            t1=t2=0;int tmp=i;
            while (tmp>1)
            {
                int p=low[tmp];s1[++t1]=1;
                while (tmp%p==0) tmp/=p,s1[t1]*=p;
            }
            dfs(1,0);
        }
        printf("%d\n",ans);
    }
}

int main()
{
    scanf("%d%d",&T,&ty);
    if (ty==1) solve1();
    else solve2();
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值