SDOI 2010--古代猪文(Lucas算法&费马小定理&中国剩余定理)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDOI 2010--古代猪文(Lucas算法&费马小定理&中国剩余定理)相关的知识,希望对你有一定的参考价值。

        发现几乎每次数论题洛谷总是让我TLE一个点。。。。

附图:

        技术分享

 

最后那个点优化了很久终于过了。。。。

       技术分享

题意

        iPig在大肥猪学校图书馆中查阅资料,得知远古时期猪文文字总个数为N。当然,一种语言如果字数很多,字典也相应会很大。当时的猪王国国王考虑到如果修一本字典,规模有可能远远超过康熙字典,花费的猪力、物力将难以估量。故考虑再三没有进行这一项劳猪伤财之举。当然,猪王国的文字后来随着历史变迁逐渐进行了简化,去掉了一些不常用的字。

        iPig打算研究古时某个朝代的猪文文字。根据相关文献记载,那个朝代流传的猪文文字恰好为远古时期的k分之一,其中k是N的一个正约数(可以是1和N)。不过具体是哪k分之一,以及k是多少,由于历史过于久远,已经无从考证了。iPig觉得只要符合文献,每一种能整除N的k都是有可能的。他打算考虑到所有可能的k。显然当k等于某个定值时,该朝的猪文文字个数为N / k。然而从N个文字中保留下N / k个的情况也是相当多的。iPig预计,如果所有可能的k的所有情况数加起来为P的话,那么他研究古代文字的代价将会是G的P次方。

       现在他想知道猪王国研究古代文字的代价是多少。由于iPig觉得这个数字可能是天文数字,所以你只需要告诉他答案除以999911659的余数就可以了。

       输入格式:

              输入文件有且仅有一行:两个数N、G,用一个空格分开。

       输出格式:

              输出文件有且仅有一行:一个数,表示答案除以999911659的余数。

Solution

         经过几天的数论的专题练习,我很快看出这道题要求的式子:

                                                                       ans=(GΣC(k,n) (k是n的因数))%999911659;

         可以用费马小定理简化:

                                                                       ans=(GΣC(k,n) (k是n的因数)%999911658)%999911659;

       因为999911658不是质数,不能直接用Lucas算法,所以分解质因数999911658=2*3*4679*35617;

         分别用Lucas算法再用中国剩余定理合并。。。。

         过程就不多讲了,之前的帖子里都说过。。。

         于是就在BZOJ上通过了。。。

代码如下

#include<iostream>
#include<cstdio>
#include<map>
#include<cmath>
#define LL long long
#define mod 999911659
#define mod2 999911658
using namespace std;
map<LL,LL> mp;
LL ny[5],a[5];
LL jc[5][20010];
LL pow(LL a,LL b,LL p){
    LL s=1;
    while(b){
        if(b&1)
            s=s*a%p;
        b>>=1;
        a=a*a%p;
    }
    return s;
}
LL C(LL a,LL b,LL p){
    if(b>a) return 0;
    if(b*2>a) b=a-b;
    LL s=1;
    for(int i=1;i<=b;i++){
        LL u=(a+i-b)%p;
        s=s*u%p*jc[mp[p]][i]%p;
    }
    return s;
}
LL Lucas(LL a,LL b,LL p){
    if(b==0) return 1;
    return C(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
}
int main(){
    LL n,g,ans=0;
    ny[1]=1;ny[2]=1;ny[3]=1353;ny[4]=31254;
    a[1]=2;a[2]=3;a[3]=4679;a[4]=35617;
    mp[2]=1;mp[3]=2;mp[4679]=3;mp[35617]=4;
    for(int i=1;i<=4;i++) jc[i][0]=1;
    for(int i=1;i<=4;i++){
        for(int j=1;j<=20000;j++)
            jc[i][j]=pow(j,a[i]-2,a[i]);
    }
    scanf("%lld%lld",&n,&g);
    if(g%mod==0) {
        printf("0\\n");
        return 0;
    }
    for(int i=1;i<=sqrt(n);i++)
        if(n%i==0){
            if(i*i==n) 
                for(int j=1;j<=4;j++){
                    LL s=ny[j]*Lucas(n,i,a[j]);
                    for(int k=1;k<=4;k++)
                        if(k!=j) s=s*a[k]%mod2;
                    ans=(ans+s)%mod2;
                }
            else
                for(int j=1;j<=4;j++){
                    LL s=ny[j]*Lucas(n,i,a[j]);
                    LL s2=ny[j]*Lucas(n,n/i,a[j]);
                    for(int k=1;k<=4;k++)
                        if(k!=j){
                            s=s*a[k]%mod2;
                            s2=s2*a[k]%mod2;
                        }
                    ans=(ans+s+s2)%mod2;
                }
        }
    ans=pow(g,ans,mod);
    printf("%lld\\n",ans);
    return 0;
}

       

        然而这在洛谷上就会像之前图片上一样,会T掉一个点。。。。

        发现组合数的函数经常调用,里面还有for循环很耗时。。。

        于是稍作优化(woc搞了一上午好吧。。。)

        至于是怎么优化的也自己看吧。。。现在很心累。。。

前后对比图:

       技术分享

代码

#include<iostream>
#include<cstdio>
#include<map>
#include<cmath>
#define LL long long
#define mod 999911659
#define mod2 999911658
using namespace std;
map<LL,LL> mp;
LL ny[5],a[5],top;
LL jc[5][40010];

LL pow(LL a,LL b,LL p){
    LL s=1;
    while(b){
        if(b&1)
            s=s*a%p;
        b>>=1;
        a=a*a%p;
    }
    return s;
}
LL C(LL a,LL b,LL p) {
    if (a<b) return 0;
    return jc[mp[p]][a]*pow(jc[mp[p]][b]*jc[mp[p]][a-b],p-2,p)%p;
}
LL Lucas(LL a,LL b,LL p){
    if(!b) return 1;
    return C(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
}
int main(){
    LL n,g,ans=0;
    ny[1]=1;ny[2]=1;ny[3]=1353;ny[4]=31254;
    a[1]=2;a[2]=3;a[3]=4679;a[4]=35617;
    mp[2]=1;mp[3]=2;mp[4679]=3;mp[35617]=4;
    for (int i=1;i<=4;i++) {
        jc[i][0]=1;
        for (int j=1;j<=a[i];j++)
            jc[i][j]=jc[i][j-1]*j%a[i];
    }
    scanf("%lld%lld",&n,&g);
    g=g%mod;
    if(!g) {
        printf("0\\n");
        return 0;
    }
    LL d=sqrt(n);
    for(int i=1;i<=d;i++)
        if(n%i==0){
            if(i*i==n) 
                for(int j=1;j<=4;j++){
                    LL s=ny[j]*Lucas(n,i,a[j]);
                    s=mod2/a[j]*s%mod2;
                    ans=(ans+s)%mod2;
                }
            else
                for(int j=1;j<=4;j++){
                    LL s=ny[j]*Lucas(n,i,a[j]);
                    LL s2=ny[j]*Lucas(n,n/i,a[j]);
                    s=mod2/a[j]*(s+s2)%mod2;
                    ans=(ans+s)%mod2;
                }
        }
    ans=pow(g,ans,mod);
    printf("%lld\\n",ans);
    return 0;
}

       

This passage is made by Yukino.

以上是关于SDOI 2010--古代猪文(Lucas算法&费马小定理&中国剩余定理)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1951[Sdoi2010]古代猪文 Lucas定理+CRT

SDOI 2010 古代猪文

「SDOI2010」古代猪文 Lucas+中国剩余定理

P2480 [SDOI2010]古代猪文 Lucas+CRT合并

[BZOJ 1951][Sdoi2010]古代猪文(Lucas+CRT+费马小定理)

[SDOI2010]古代猪文