zoukankan      html  css  js  c++  java
  • 洛谷 P2155 BZOJ 2186 codevs 2301 [SDOI2008]沙拉公主的困惑

    题目描述

    大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。//codevs这里有坑,R是合数

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1:
    1 11
    4 2
    输出样例#1:
    1
    
    数据范围:
    对于100%的数据,1 < = N , M < = 10000000

    解题思路

      http://www.cnblogs.com/yangyaojia/p/6434611.html

      http://blog.csdn.net/loi_dqs/article/details/50520309

    吐槽

      这题一题能抵三题做了。

      不想被卡常就离线吧,筛法筛到最大的n即可,否则卡常、卡内存太严重了,我的好不容易洛谷ac,跑了6s。

      求逆元可以线性递推,也可以扩欧。

      popoqqq大神博客的代码(https://www.luogu.org/record/show?rid=2565704)、黄学长博客(https://www.luogu.org/record/show?rid=2565713)里线性递推逆元的代码都在洛谷爆零,不知为啥。

      我的代码离线int会溢出,估计是筛法出问题,还不够优化,于是递推求逆元的数组开不下了,就用扩欧吧。

      卡一波常1.3s跑完(BZOJ不开O2跑了2.9s)。

    源代码

    6s的,求逆元用线性递推(BZOJ TLE)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    int T,R,n,m;
    long long  jc[10000010]={0};//阶乘取模
    long long inv[10000010]={0};//逆元
    bool not_prime[10000010]={0};//筛子
    long long ans[10000010]={0};//pi累乘
    
    void init()
    {
        inv[1]=1;
        for(int i=2;i<=10000000&&i<R;i++)
            inv[i]=(R-R/i)%R*inv[R%i]%R;
        ans[1]=1;jc[1]=1;
        for(int i=2;i<=10000000;i++)
        {
            jc[i]=jc[i-1]*i%R;
            if(!not_prime[i])
            {
                ans[i]=ans[i-1]*(i-1)%R*inv[i%R]%R;
                for(int j=i<<1;j<=10000000;j+=i)
                    not_prime[j]=1;
            }
            else
                ans[i]=ans[i-1];
        }
    }
    
    int main()
    {
        scanf("%d%d",&T,&R);
        init();
        while(T--)
        {
            scanf("%d%d",&n,&m);
            printf("%lld
    ",(long long)(jc[n])*(long long)(ans[m])%R);
        }
        return 0;
    }

    1.3s的,求逆元用扩欧

    #include<cstdio>
    #include<algorithm>
    int T,R,maxn=-1;
    long long  jc[10000010]={0};//阶乘取模
    //int inv[10000010]={0};//逆元
    bool not_prime[10000010]={0};//筛子
    long long ans[10000010]={0};//pi累乘
    int n[10000010]={0},m[10000010]={0};
    
    
    void exgcd(int a,int b,int &x,int &y)
    {
        if(b==0){x=1;y=0;return;}
        exgcd(b,a%b,x,y);
        int t=x;x=y;y=t-a/b*y;
    }
    inline int getine(int t)
    {
        int x,y;
        exgcd(t,R,x,y);
        return (x%R+R)%R;
    }//扩欧求逆元
    
    inline void Read(int &in){
        static char ch;
        for(ch=getchar();ch>'9'||ch<'0';ch=getchar()) ;
        for(in=0;ch>='0'&&ch<='9';ch=getchar()) in=in*10+ch-'0';
    }
    void init()
    {
        /*inv[1]=1;
        for(int i=2;i<=maxn&&i<R;i++)
            inv[i]=(R-R/i)%R*inv[R%i]%R;*///线性递推求逆元
        ans[1]=1;jc[1]=1;
        for(int i=2;i<=maxn;i++)
        {
            jc[i]=jc[i-1]*i%R;
            if(!not_prime[i])
            {
                ans[i]=ans[i-1]*(i-1)%R*getine(i%R)%R;
                for(int j=i<<1;j<=maxn;j+=i)
                    not_prime[j]=1;
            }
            else
                ans[i]=ans[i-1];
        }
    }
    
    int main()
    {
        Read(T);Read(R);
        for(register int i=1;i<=T;i++)
             Read(n[i]),Read(m[i]),maxn=std::max(maxn,n[i]);
        init();
        for(register int i=1;i<=T;i++)
            printf("%lld
    ",jc[n[i]]*ans[m[i]]%R);
        return 0;
    }

    洛谷记录里最优的代码,用户ID小小莫。Orz,洛谷跑了0.9s,BZOJ不开O2跑了2.6s,素数筛法值得学习。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    static const int maxm = 10000 + 50;
    static const int maxx = 10000000 + 50;
    typedef long long LL;
    bool Notprime[maxx];
    int inv[maxx],phi[maxx],fac[maxx],prime[maxx];
    int T,n[maxm],m[maxm],p,cnt,tot;
    
    void Read(int &);
    
    inline void Linear_shaker(){
        int maxn = tot+1;
        fac[1]=1;inv[1]=1;phi[1]=1;
        for(register int i=2;i<maxn;i++){
            if(!Notprime[i])
                prime[++cnt]=i;
            for(register int j=1;j<=cnt && i*prime[j]<maxn;j++){
                Notprime[i*prime[j]]=1;
                if(i%prime[j]==0) break;
            }
        }
        for(register int i=2;i<=maxn;i++){
            fac[i] = ((LL)fac[i-1]*i)%p;
            if(i<p) inv[i] = (LL)(p-p/i)*inv[p%i]%p;
            if(!Notprime[i]) phi[i] = (LL)phi[i-1]*(i-1)%p*inv[i%p]%p;
            else phi[i] = phi[i-1];
        }
    }
    int main(){
        Read(T),Read(p);
        for(register int i=1;i<=T;i++) Read(n[i]),Read(m[i]),tot = max(tot,n[i]);
        Linear_shaker();
        for(register int i=1;i<=T;i++)
            printf("%lld
    ",(LL)fac[n[i]]*phi[m[i]]%p);
        return 0;
    }
    
    void Read(int &in){
        static char ch;
        for(ch=getchar();ch>'9'||ch<'0';ch=getchar()) ;
        for(in=0;ch>='0'&&ch<='9';ch=getchar()) in=in*10+ch-'0';
    }
  • 相关阅读:
    网络知识 ACL NAT IPv6
    const用法
    单向链表排序
    文件系统
    protel DXP的类矢量图功能
    proteus画元件
    SD卡FAT32文件系统格式
    如何实现一个malloc函数
    sbrk and coreleft
    windows下常用快捷键
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/7174421.html
Copyright © 2011-2022 走看看