bzoj3240 [Noi2013]矩阵游戏——费马小定理+推式子

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3240

n 和 m 太过巨大,不难想到应该用费马小定理什么的来缩小范围;

总之就是推式子啦,看博客:https://blog.csdn.net/jiangshibiao/article/details/24594825

还有:https://www.cnblogs.com/iiyiyi/p/5617598.html

其实也蛮好推的,也挺好写,但我调了很久很久啊...

要十分注意取 mod 时候加括号的艺术...

还要注意指数里的 n 或 m 取的是 mod-1 的模,就是费马小定理。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int const maxl=1e6+5;
ll a,b,c,d,mod=1e9+7,A,B,tmp;
char nn[maxl],mm[maxl];
struct N{ll ord,uni;}n,m;
void get()
{
    int l=strlen(nn);
//    for(int i=l-1;i>=0;i--)//傻了 
    for(int i=0;i<l;i++)
    {
        n.ord=(n.ord*10%mod+nn[i]-'0')%mod;//a=1
        n.uni=(n.uni*10%(mod-1)+nn[i]-'0')%(mod-1);//a!=1
    }
    l=strlen(mm);
//    for(int i=l-1;i>=0;i--)
    for(int i=0;i<l;i++)
    {
        m.ord=(m.ord*10%mod+mm[i]-'0')%mod;//a=1
        m.uni=(m.uni*10%(mod-1)+mm[i]-'0')%(mod-1);//a!=1
    }
}
ll pw(ll a,ll b)
{
    ll ret=1;
    for(;b;b>>=1ll,(a*=a)%=mod)
        if(b&1) (ret*=a)%=mod;
    return ret;
}
ll ni(ll x){return pw(x,mod-2);}
int main()
{
    scanf("%s%s",&nn,&mm);
    scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
    get();
    if(a==1)
    {
        B=(((c*b)%mod*(m.ord-1))%mod+d)%mod;
        if(c==1)tmp=(1+n.ord*B)%mod;
        else tmp=(pw(c,n.uni)+((B*(pw(c,n.uni)-1)%mod)*ni(c-1))%mod)%mod;//注意指数部分是uni而非ord!!! 
    }
    else
    {
        A=(pw(a,m.uni-1)*c)%mod;
        B=(((((pw(a,m.uni-1)-1)*ni(a-1))%mod*c)%mod*b)%mod+d)%mod;
        tmp=(pw(A,n.uni)+(((pw(A,n.uni)-1)*ni(A-1))%mod*B)%mod)%mod;
    }
    printf("%lld",((tmp-d)*ni(c)%mod+mod)%mod);//+mod %mod
    return 0;
}

 

posted @ 2018-07-02 19:40  Zinn  阅读(178)  评论(0编辑  收藏  举报