【算法】【倍增】

0 阅读1分钟

atcoder.jp/contests/ty…

#include <bits/stdc++.h>  
using namespace std;  
using ll=long long;  
const int M=1e9+7;  
const int lg=62;  
int main(){  
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);  
    ll n,b,k;cin>>n>>b>>k;  
    vector<int>c(k);  
    for(auto& ci:c)cin>>ci;  
    auto mul=[&](const vector<ll>&dpi,const vector<ll>& dpj,ll tj)->vector<ll>{  
        vector <ll>res(b,0);  
        for(ll p=0;p<b;p++){  
            for(ll q=0;q<b;q++){  
                int nr=(p*tj+q)%b;  
                res[nr]=(res[nr]+dpi[p]*dpj[q])%M;  
            }  
        }return res;  
    };  
    // ten[i] = 10^(2^i) mod B  
    vector<ll>ten(lg,10);  
    for(int i=1;i<lg;i++){  
        // 10^(2^i) = (10^(2^(i-1)))^2  
        ten[i]=(ten[i-1]*ten[i-1])%b;  
    }vector<vector<ll>>db(lg,vector<ll>(b,0));  
    for(int k1=0;k1<k;k1++){  
        db[0][c[k1]%b]+=1;//一位的余数是本身  
    }for(int i=1;i<lg;i++){  
        db[i]=mul(db[i-1],db[i-1],ten[i-1]);  
        //2 位数字 = 1 位数字 + 1 位数字 拼接起来  
        //4 位数字 = 2 位数字 + 2 位数字 拼接起来  
        //8 位数字 = 4 位数字 + 4 位数字 拼接起来  
    }vector<ll>res(b,0);  
    res[0]=1;  
    //把 N 拆成二进制,然后用「拼接」的方式,拼出一个 N 位的数字!  
    for(int i=0;i<lg;i++){  
        if(n&(1ll<<i)){  
            res=mul(res,db[i],ten[i]);  
        }  
    }cout<<res[0]<<endl;  
}

思路

1.dp基础:求这种问题,dp[i + 1][(r * 10 + c[k]) % B] += dp[i][r]

此时的时间复杂度是 O(NB)。直接计算一定会超时(TLE)。

2.就算用矩阵快速幂优化到 O(B3logN),在 B=1000 时依然无法通过。

3.使用倍增法后,时间复杂度可以优化到 O(B2logN)

为什么倍增

N 巨大到 1e18,根本不能一位一位递推,只能用 log 级别的方法,而倍增就是专门干这个的。