[SDOI2008]沙拉公主的困惑 线性筛 素数+欧拉

[SDOI2008]沙拉公主的困惑 线性筛 素数+欧拉

题目大意

给定n,m,求在1到n!内与m!互质的个数,答案要对r取模。

输入格式:
第一行为两个整数T,R。R<=10^9+~~10,T<=10000,表示该组中测试数据数目,R为模 后面T行,每行一对整数n,m,见题目描述 m<=n

输出格式:
共T行,对于每一对N,M,输出1至N!中与M!素质的数的数量对R取模后的值

输入输出样例
input
1 11
4 2
output
1

解题分析
首先,我们来引出一个定理
如果a与b互质,那么 bk+a 也与b互质。证明和证明gcd的证明类似。
反过来,我们也可以用 gcd 证明,
因为 gcd(a,b)=1 ,所以 gcd(a%b,b)=1
因为 a%b=akb ,故 gcd(akb,b)=1 ,及 akb b 互质。
根据这个特性,并且n>=m,所以可以将n!分成若干段,每段为m!,每一段中与m!互质的个数都是相等的且等于1到m!中与m!互质的个数
我们可以得到式子

ans=n!m!ϕ(m!)

进一步拆开,我们可以得到 (假设p为m!的质因数,很容易可以知道,p就是所有小于m的素数,r为质因数个数)

ans=n!m!m!i=1r(pi1)i=1rpians=n!i=1r(pi1)i=1rpi

因为 ans modR ,所以我们也要算1到m的逆元,在累乘 i=1rpi ,乘的是 pi 的逆元。
有多组询问,我们得先预处理一些数据,累乘的时候要%R
我们令 k[i]=i!,inv[i] 为i的逆元, f1[i]=a=1i(pa1)
,f2[i]=a=1ipa
ans=f1[m]f2[m]k[n]
先预处理O()答案,对于询问O(1)出解

#include <cstdio>
#include <iostream>
#include <math.h>
using namespace std;
const int MAXN=10000000+10;
bool su[MAXN];
int q[MAXN][2],maxm,maxn,t,inv[MAXN],p,n,m;
int k[MAXN],f1[MAXN],f2[MAXN],ans=0;
void work()
{

  inv[1]=1;k[1]=1;f1[1]=1;f2[1]=1;
  for(int i=2;i<=sqrt(maxm);i++) if(!su[i])
    for(int j=2;j<=maxm/i;j++) su[i*j]=1;

    for(int i=2;i<=maxn;i++)
  {
      if(i<=maxm)
    {    
        inv[i]=(1LL*-(p/i)*inv[p%i])%p;
      inv[i]=(inv[i]%p+p)%p;
    }
      if(i<=maxm)
        {
          if(!su[i])
        {
            f1[i]=(1LL*f1[i-1]*((i-1)%p))%p;
          f2[i]=(1LL*f2[i-1]*(inv[i]%p))%p;    
        }else
        {    
          f1[i]=f1[i-1];
          f2[i]=f2[i-1];
        }
        }
    k[i]=(1LL*k[i-1]*(i%p))%p;
  }
}
int main()
{
  scanf("%d%d",&t,&p);

  for(int i=1;i<=t;i++)
  {
      scanf("%d%d",&q[i][0],&q[i][1]);
    maxn=max(maxn,q[i][0]);
    maxm=max(maxm,q[i][1]);
  }
  work();
  for(int i=1;i<=t;i++)
    {
      ans=((1LL*k[q[i][0]]%p)*1LL*(f1[q[i][1]]%p))%p;
    ans=(ans*1LL*(f2[q[i][1]]%p))%p;
    printf("%d\n",ans);
  }

  return 0;
}
/*
2 11
6 3
10 5

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值