2019 ICPC 南昌网络赛 H. The Nth Item (二阶线性数列递推)

The Nth Item

题解
比赛时我还没解完,就被用矩阵快速幂加map过掉了。。但还是补上这个。
二阶线性数列递推,经典解法:
F ( n ) = 3 F ( n − 1 ) + 2 F ( n − 2 ) F(n)=3F(n-1)+2F(n-2) F(n)=3F(n1)+2F(n2)

我们令 F ( n ) = r n F(n)=r^n F(n)=rn

所以特征多项式为:
r 2 − 3 r − 2 = 0 r^2-3r-2=0 r23r2=0
解得 r 1 = 3 + 17 2 , r 2 = 3 − 17 2 r_1=\cfrac{3+\sqrt{17}}{2},r_2=\cfrac{3-\sqrt{17}}{2} r1=23+17 ,r2=2317

所以
F ( n ) = c 1 r 1 n + c 2 r 2 n F ( n ) = c 1 ( 3 + 17 2 ) n + c 2 ( 3 − 17 2 ) n \begin{aligned}F(n)&=c_1r_1^n+c_2r_2^n\\F(n)&=c_1(\cfrac{3+\sqrt{17}}{2})^n+c_2(\cfrac{3-\sqrt{17}}{2})^n\end{aligned} F(n)F(n)=c1r1n+c2r2n=c1(23+17 )n+c2(2317 )n
由题目中给出的 F ( 0 ) = 0 , F ( 1 ) = 1 F(0)=0,F(1)=1 F(0)=0,F(1)=1
F ( 0 ) = c 1 + c 2 = 0 F ( 1 ) = c 1 ( 3 + 17 2 ) + c 2 ( 3 − 17 2 ) = 1 \begin{aligned}F(0)&=c_1+c_2=0\\F(1)&=c_1(\cfrac{3+\sqrt{17}}{2})+c_2(\cfrac{3-\sqrt{17}}{2})=1\end{aligned} F(0)F(1)=c1+c2=0=c1(23+17 )+c2(2317 )=1
解得 c 1 = 1 17 , c 2 = − 1 17 c_1=\cfrac{1}{\sqrt{17}},c_2=-\cfrac{1}{\sqrt{17}} c1=17 1,c2=17 1


F ( n ) = 1 17 ( ( 3 + 17 2 ) n − ( 3 − 17 2 ) n ) F(n)=\cfrac{1}{\sqrt{17}}((\cfrac{3+\sqrt{17}}{2})^n-(\cfrac{3-\sqrt{17}}{2})^n) F(n)=17 1((23+17 )n(2317 )n)

上面是推导过程,具体算法还涉及到q次询问,所以普通快速幂是不能实现的,时间复杂度过高,题解说的是预处理 r 0 , r 1 , ⋯   , r 1 e 9 r^0,r^1,\cdots,r^{\sqrt{1e9}} r0,r1,,r1e9 r 0 ⋅ 1 e 9 , r 1 ⋅ 1 e 9 , ⋯   , r 1 e 9 ⋅ 1 e 9 r^{0·\sqrt{1e9}},r^{1·\sqrt{1e9}},\cdots,r^{\sqrt{1e9}·\sqrt{1e9}} r01e9 ,r11e9 ,,r1e9 1e9 ,做到了 O ( 1 ) O(1) O(1)的询问。

我是按照题解写的预处理,但是我觉得应该可以k进制快速幂, l o g 2 ( n ) log_2(n) log2(n)不够,就把2换成大一点的嘛,总归是可以的。

代码

// #include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define me(x,y) memset((x),(y),sizeof (x))
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define SGN(x) ((x)>0?1:((x)<0?-1:0))
#define ABS(x) ((x)>0?(x):-(x))                                                                                                                                                     
// #define int __int128
 
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
 
const int maxn = 1e6+10;
const int inf = __INT32_MAX__;
const ll INF = __LONG_LONG_MAX__;
const ll MOD = 998244353;
const double eps = 1e-10;
const double PI = std::acos(-1);

const ll ans17 = 524399943;    //17的二次剩余
const int top = 1e9;
const int k = (int)sqrt(top);
ll qpow(ll a,ll b,ll p){
    ll ans = 1;
    while(b){ if(b&1) ans = ans*a%p; a=a*a%p;b >>= 1;}
    return ans;
}
ll cnt1[maxn],cnt2[maxn],cnt11[maxn],cnt22[maxn];
const ll p2 = qpow(2,MOD-2,MOD);  //2的逆元
const ll p17 = qpow(ans17,MOD-2,MOD); //sqrt(17)的逆元
const ll r1 = (3+ans17+MOD)*p2%MOD;//两个根
const ll r2 = (3-ans17+MOD)*p2%MOD;
void init(){
    for(int i = 0; i*i <= top; ++i) cnt1[i] = qpow(r1,i,MOD),cnt2[i] = qpow(r2,i,MOD);
    ll r11 = cnt1[k],r22 = cnt2[k];
    for(int i = 0; i*i <= top; ++i) cnt11[i] = qpow(r11,i,MOD),cnt22[i] = qpow(r22,i,MOD);
}
map<ll,ll> mp;
ll f(ll n){
    if(mp.count(n)) return mp[n];	//map记录
    ll pre=0;
    ll c = n/k,r = n%k;
    pre = (cnt11[c]*cnt1[r]%MOD-cnt22[c]*cnt2[r]%MOD+MOD)%MOD;
    pre = pre*p17%MOD;
    mp[n] = pre;
    return pre;
}
int main(){
    ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    freopen("1in.in","r",stdin);
    freopen("1out.out","w",stdout);
#endif

    init();
    ll n,q;
    cin>>q>>n;
    ll ans=0,fg=0;
    while(q--){
        ll pre = f(n%(MOD-1));	//n可能过大,欧拉降幂一下
        n = n^(pre*pre);
        ans = ans^pre;
        // cout<
    }
    cout<<ans%MOD<<endl;
    return 0;
}

你可能感兴趣的:(——数学——,#,数论——欧拉,#,数学——差分)