Best Solver HDU - 5451(共轭构造+循环节+快速幂)

Best Solver HDU - 5451

The so-called best problem solver can easily solve this problem, with his/her childhood sweetheart.

It is known that y=(5+26)1+2x. y = ( 5 + 2 6 ) 1 + 2 x .
For a given integer x(0x<232) x ( 0 ≤ x < 2 3 2 ) and a given prime number M (M≤46337), print [y]%M. ([y] means the integer part of y
)
Input
An integer T (1<T1000) ( 1 < T ≤ 1000 ) , indicating there are T test cases.
Following are T lines, each containing two integers x and M
, as introduced above.
Output
The output contains exactly T lines.
Each line contains an integer representing [y]%M
.
Sample Input

7
0 46337
1 46337
3 46337
1 46337
21 46337
321 46337
4321 46337

Sample Output

Case #1: 97
Case #2: 969
Case #3: 16537
Case #4: 969
Case #5: 40453
Case #6: 10211
Case #7: 17947
题意:

求解 y=[(5+26)1+2x]%M,[] y = [ ( 5 + 2 6 ) 1 + 2 x ] % M , [ ] 指 取 整 数 部 分

分析:

当我们看到带有根号的数的幂的时候,应该联想到斐波那契数列

因为斐波那契数列通项公式:

fn=15(1+52)n15(152)n f n = 1 5 ( 1 + 5 2 ) n − 1 5 ( 1 − 5 2 ) n

递推式:
fn=fn1+fn2 f n = f n − 1 + f n − 2

因此我们应该想到需要构造类似于斐波那契数列通项公式这样的共轭式子来看能发现什么

我们构造

an=(5+26)n+(526)n a n = ( 5 + 2 6 ) n + ( 5 − 2 6 ) n

类比于斐波那契数列通项公式我们知道 an a n 必为整数,因为无理数在二项式展开后会相互消去

简单证明我们可以展开 an a n

an=Cin5i(26)ni+Cin5i(1)ni(26)ni a n = C n i 5 i ⋅ ( 2 6 ) n − i + C n i 5 i ⋅ ( − 1 ) n − i ⋅ ( 2 6 ) n − i

ni n − i 为偶数的时候 (26)ni ( 2 6 ) n − i 变成整数, (1)ni=1 ( − 1 ) n − i = 1
ni n − i 为奇数的时候 (1)ni=1 ( − 1 ) n − i = − 1 和前面的恰好抵消

然后我们发现

05261 0 ≤ 5 − 2 6 ≤ 1

an a n 是一个整数也就说明假设 (5+26)n=k.,(526)n=0. ( 5 + 2 6 ) n = k . ⋯ , ( 5 − 2 6 ) n = 0. ⋯

an=k.+0.=k+1 a n = k . ⋯ + 0. ⋯ = k + 1

(小数部分互补,使得整数部分+1)

而我们要求得的答案实际上是 k k

因此我们得到答案

ans=an1

所以我们只要求出数列 an a n 来即可

继续类比斐波那契数列的递推公式

我们可以知道 an a n 的递推式必定也为

an=pan1+qan2 a n = p ⋅ a n − 1 + q ⋅ a n − 2

的形式

所以我们可以手动求出前几项带入求解系数 p,q p , q

但我们也有更高级的计算方法,详细请看斐波那契数列通项公式推导及其类似构造共轭公式反求递推式

最终得到递推式

an=10an1an2 a n = 10 ⋅ a n − 1 − a n − 2

我们发现指数可能会非常大,但是题目却要求的取模。

所以我们再次对比斐波那契数列取模具有循环节的性质,这个数列取模也必含有循环节

所以对于每个给定的模数 M M 我们每次可以打表找出循环节r,然后求解系数 (1+2x)%r ( 1 + 2 x ) % r

就得到了在一个循环节中的位置,也就是得到了数列 an a n 的第几项

然后输出 an1 a n − 1 取模即可

code:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const int maxn = 47000;
ll x;
int m,r[maxn],a[maxn];
void init(){
    if(r[m] != -1)
        return;
    a[0] = 2 % m;
    a[1] = 10 % m;
    for(int i = 2;;i++){
        a[i] = (10 * a[i-1] - a[i-2] + m) % m;
        if(a[i-1] == a[0] && a[i] == a[1]){
            r[m] = i-1;
            break;
        }
    }
}
ll q_pow(ll b,ll mod){
    ll ans = 1LL;
    ll aa = 2LL;
    while(b){
        if(b & 1)
            ans = ans * aa % mod;
        b >>= 1;
        aa = aa * aa % mod;
    }
    return ans;
}
void solve(){
    int n,ans;
    n = (int)q_pow(x,(ll)r[m]);
    n = (n + 1) % r[m];
    a[0] = 2 % m;//注意这里需要再求一边,因为如果r[m]之前已经算过了,将不初始化a数组,所以里面的值是上一个模数下的值
    a[1] = 10 % m;
    for(int i = 2; i <= n; i++){
        a[i] = (10 * a[i-1] - a[i-2] + m) % m;
    }
    ans = (a[n] - 1 + m) % m;
    printf("%d\n",ans);
}
int main(){
    memset(r,-1,sizeof(r));
    int T,cas = 0;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%d",&x,&m);
        init();
        printf("Case #%d: ",++cas);
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值