8 2018 CCPC-Wannafly Camp #4 2088: 电音之王 (快速乘法(取模))

http://newoj.acmclub.cn/contests/1392/problem/1

题目描述:

终于活成了自己讨厌的样子。

听说多听电音能加快程序运行的速度。

定义一个数列,告诉你a\_0,a\_1,m\_0,m\_1,ca0​,a1​,m0​,m1​,c,定义a\_n=m\_0a\_{n-1}+m\_1a\_{n-2}+can​=m0​an−1​+m1​an−2​+c对所有n\geq 2n≥2。

求\left( \prod\_{i=0}^{k}{a\_i} \right) mod M(∏i=0k​ai​)modM

输入:

第一行一个整数T(1\leq T\leq 1000)T(1≤T≤1000),表示数据组数。

每组数据一行77个整数a\_0,a\_1,m\_0,m\_1,c,M,ka0​,a1​,m0​,m1​,c,M,k,保证1\leq M\leq 10^{18},0\leq a\_0,a\_1,m\_0,m\_1,c< M, 2\leq k\leq 10^61≤M≤1018,0≤a0​,a1​,m0​,m1​,c<M,2≤k≤106,保证MM为奇数。

保证\sum k \leq 10^8∑k≤108。

输出:

对于每组数据,输出一行表示答案。

样例输入

1
1 1 1 1 0 1000000007 10

样例输出

904493530

这一道题目看上去就是一道简单的连乘的题目,但是细心的话会发现数字是非常的大所以会有当被取的余数非常大的话就

会出现溢出的情况,所以我们在计算的时候要考虑到这一点情况,否则最后求出的数字肯定是不正确的;

处理方案一:这种方法可能会超时,补题的时候没有用,不知道会不会超时,它的一次乘法的时间复杂度是log()的,就是将

连乘的其中一个数字用二进制的数字来表示,这样的话就可以在计算的时候枚举用二进制表示的数字的每一位将乘法转化为加法并且可以边加边取模,这样的话就不会出现溢出的情况但是时间是很爆照的;

处理方案二: 这个处理的方法是很骚的就是可以用o(1)的时间复杂度将我们要的连乘给算出来并且可以防止溢出;

typedef   ll ksc( ll a, ll  b,  ll mod )   //前面的两个数字分别是我们要用乘法来连乘得数字,后面的一个是我们要取模的数字;

 {

 return (    a*b  - (ll) ( (long double )  a/mod*b) * mod+mod    )%mod;

}

虽然我不知道这是什么原理但是这个样子的话是真的可以把题目给做出来;

#include <bits/stdc++.h>
using namespace std;
#define rep(i,s, n) for(int i=s;i<=n;i++) 
#define per(i,n,s)  for(int i=n;i>=s;i--)
const int Max = 2e6+10; 
typedef long long ll;
ll  a[Max],a0,a1,m0,m1,c,mod,k;
ll ksc(ll a,  ll b,  ll mod){
    return (a*b-(ll)((long double )a/mod*b)*mod+mod)%mod;
}
int t;
int main(){
    scanf("%d",&t);
    while(t--) {
        scanf("%lld %lld %lld %lld %lld %lld %lld",&a0,&a1,&m0,&m1,&c,&mod,&k);
        m0%=mod;
        m1%=mod;
        a0%=mod;
        a1%=mod;
        c%=mod;
        a[0]=a0;
        a[1]=a1;
        ll sum=ksc(a[0],a[1],mod);
        ll ans1,ans2;
        sum=(sum%mod+mod)%mod;
        rep(i,2,k){
            ans1=ksc(m0,a[i-1],mod);
            ans2=ksc(m1,a[i-2],mod);
            a[i]=ans1+ans2+c;
            a[i]=(a[i]%mod+mod)%mod;
            sum=ksc(sum,a[i],mod);
            sum=(sum%mod+mod)%mod;
        }
        printf("%lld\n",(sum%mod+mod)%mod);
    }
    return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值