[CQOI2015]&[bzoj3933]多项式 二项式定理+高精度

题目链接:bzoj3933 & CQOI2015多项式.

—————————————-

概述

题目大意如下。
已知整数 n,t 以及数列 ak ,求数列 bk 的表达式使得下式恒成立:

k=0nakxk=k=0nbk(xt)k.

其中,数列 ak 的递推式为:

ak={(1234×ak1+5678)mod 3389,1,k>0k=0

告诉你整数 m ,输出bm的值。( 1n103000, 0t10000, 0nm5

—————————————-

分析

我们观察这个式子:

k=0nakxk=k=0nbk(xt)k.

假如这个式子恒成立,那么就说明 bk 的取值与自变量 x 无关,而只跟已知量akt有关。

所以我们先考虑把已知数都放到等式的同一边,也就是将 x 带入x+t,有:

k=0nak(x+t)k=k=0nbkxk.

对于左边的 (x+t)k ,我们用二项式定理展开:

k=0nak(x+t)k=k=0naki=0kCikxitki.

然后利用恒等变换考虑先枚举 x 的贡献:

k=0naki=0kCikxitki=k=0nxki=knCkiaitik.

至此,我们得到了:

k=0nxki=knCkiaitik=k=0nbkxk.

这时我们发现:等式两边自变量 x 的影响都是一样的,假如我们强制令bk=ni=kCkiaitik,那么两边的 xk 便可以消去了,所以有:

bm=i=mnCmiaitim.

进一步变形:

bm=i=0nmCmm+iam+iti.

由于题目中说 nm5 ,那么这个式子直接 for 循环枚举就可以了。其中, Cmm+i ti 都可以 O(i) 计算,计算量不超过5次,暴力即可。那么现在问题的关键就是如何快速求出 am+i 了。

其实非常简单,高中数学内容。

我们观察一下 an 的递推式:

ak={(1234×ak1+5678)mod 3389,1,k>0k=0

我们先不考虑 mod 3389 ,易得:

ak=1234×ak1+5678.

进而易得:

ak1=1234×ak2+5678.

ak1 的表达式带入 ak 的表达式:

  ak=1234×(1234×ak2+5678)+5678.

ak=12342×ak2+12345678+5678.

继续迭代可得:

ak=1234i×aki+5678×j=0i11234j.

带入 i=k ,可得 ak 的通项:

ak=1234k+5678×j=0k11234j. (mod 3389)

其中,可知 1234k=1234k mod φ(3389) ,可以快速幂计算;而 k1j=01234j 就是等比数列求和,可以 O(1) 计算。

至此式子就分析完了。

那么 n103000 怎么办?还能怎么办,高精度慢慢调呗。。。

—————————————-

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define For(i, j, k) for(int i = j; i <= (int)k; ++ i)
#define Forr(i, j, k) for(int i = j; i >= (int)k; -- i)
#define INF 0x3f3f3f3f
using namespace std;

const ll base = 1e9;//压位,压9位.
const int mo = 3389;
const int maxn = 3000 + 5;

char s[maxn];

struct Big{
    int len;
    ll a[4005];

    inline void init(int x){
        memset(a, 0, sizeof(a));
        if(x)    a[len = 1] = x;
        else len = 0;
    }

    inline void get(){
        int l;    ll temp;
        memset(a, 0, sizeof(a));
        scanf("%s", s + 1);    l = strlen(s + 1);
        Forr(i, l, 1){
            if((l - i) % 9 == 0)    temp = 1;
            a[(l-i)/9+1] += 1ll * (s[i]^48) * temp;    temp *= 10;
        }
        len = (l-1)/9 + 1;
    }

    inline Big operator + (Big o)const{
        Big back;    int l;
        back.init(0);    l = max(len, o.len);
        For(i, 1, l){
            back.a[i] += a[i] + o.a[i];
            back.a[i+1] += back.a[i]/base,   back.a[i] %= base;
            if(back.a[l+1])    ++ l;
        }
        back.len = l;    return back;
    }

    inline ll operator - (Big o)const{
        ll back = 0;
        Forr(i, len, 1)
            back = back * base + (a[i]-o.a[i]);
        return back;
    }

    inline Big operator * (Big o)const{
        Big back;    int l;
        back.init(0);    l = len + o.len - 1;
        For(i, 1, len)
            For(j, 1, o.len){
                back.a[i+j-1] += a[i] * o.a[j];
                back.a[i+j] += back.a[i+j-1]/base,    back.a[i+j-1] %= base;
                if(back.a[l+1])    ++ l;
            }
        back.len = l;    return back;
    }

    inline Big operator / (int o)const{
        Big back;    int l;
        back = *this;    l = len;
        Forr(i, l, 1)
            back.a[i-1] += (back.a[i]%o) * base,   back.a[i] /= o;
        back.a[0] = 0;        while(!back.a[l] && l)    -- l;
        back.len = l;    return back;
    }

    inline ll operator % (int o)const{
        ll back = 0;
        Forr(i, len, 1)
            back = (back * base + a[i]) % o;
        return back;
    }

    inline void print(){
        if(!len)    puts("0");
        else{
            while(!a[len])    --len;
            printf("%lld",a[len]);
            Forr(i, len-1, 1)    printf("%09lld", a[i]);
        }
    }
}n, t, m, C, p, Ans, temp, tmpp;//工业代码高精度.

inline int ksm(int x, int y){
    int back = 1;
    while(y){
        if(y & 1)    back = back * x % mo;
        x = x * x % mo;
        y >>= 1;
    }
    return back;
}//快速幂.

inline int A(Big x){
    return (209 * ksm(1234, x % (mo-1)) % mo + 3181) % mo;
}//求ax的值.

int main(){
    int tmp;
    n.get();  t.get();  m.get();

    tmp = n - m;    C.init(1);  p.init(1);  Ans.init(0);  temp.init(1);
    for(int i = 0; i <= tmp; ++ i, m = m + temp){
        if(i)    C = (C * m) / i,   p = p * t;
        tmpp.init(A(m));
        Ans = Ans + C * p * tmpp;//C:组合数;p:t^i次方;tmpp:am的值.
    }

    Ans.print();
    return 0;
}

—————————————-

小结

这题的式子推导难度并不是很大,想到了消去自变量x的影响就基本上解决的这道题。估计本题最大的难度还是在于高精度的调试吧2333.

—————————————-

wrote by miraclejzd.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值