总理同学的编程尝试

为大家见证传说中的初学者暴力编程(C/C++)

Captain obvious and the Rabbit-Man 谜之数学题

题面

这是一个中文版的简化题面,原文请–> 戳这里 <–

题目描述

输入文件

输出文件

数据范围

思路

注:下文中的Fibi表示原文中的F(i),即从Fib1=1Fib2=2开始的斐波那契数列。

发现{ak}是未知的,高斯消元O(n3)求出所有a可以得到55分。

考虑满分做法,时间复杂度大概为O(n2)

根据二项式定理的经验,感性理解一下可以得出,p(k+1)可以表示为p(1)..p(k)的线性组合。

其中p(t)=i=1kaiFibit

不妨设p(k+1)=t1p(1)+t2p(2)+..+tkp(k)

这样我们就有:

i=1kaiFibik+1=i=1ktij=1kajFibji

=i=1kj=1ktiajFibji=j=1ki=1ktiajFibji=j=1kaji=1ktiFibji

交换一下右边的循环变量i和j。

=i=1kaij=1ktjFibij

总的写一下:

i=1kaiFibik+1=i=1kaij=1ktjFibij

i:aiFibik+1=aij=1ktjFibij为上式的充分条件。

i:aiFibik+1=aij=1ktjFibiji:Fibik+1=j=1ktjFibij

这样{tk}就变得和{ak}无关了。

如果我们能构造一个{tk}使得i:Fibik+1=j=1ktjFibij,那么它显然也能使得p(k+1)=t1p(1)+t2p(2)+..+tkp(k)

i:Fibik+1=j=1ktjFibiji:Fibik+1+j=1ktjFibij=0

用变量x替换上式中的Fibi,上式转化为:

i{1,2,..,k}:f(Fibi)=0,

其中:f(x)=t1x1+t2x2+..tkxk+(xk+1)

也就是说,前k个斐波那契数为这个k+1次函数的k个零点,且最高次项系数为-1。这样的函数显然这个函数是很容易构造的:f(i)=x(xFib1)(xFib2)..(xFibk)

这个多项式的i次项系数即为ti,用系数表示法描述多项式,暴力卷积即可通过本题所有数据。

但是我认为这道题可以通过分治FFT进行优化,但对模数M必须进行进一步的约束(或者是使用任意模数的NTT解决,但是可惜我不会啊!!)。

代码

这道题的一种O(n2)的做法,其实我觉得并不完美。如果能结合分治套任意模数NTT的话可以优化成O(nlog2n)

#include <cstdio>
#include <cstring>
typedef long long LLint;
const int maxn=4000+5;

struct vec{
    int len; LLint a[maxn];
    void set(int a0,int a1){
        a[0]=a0;a[1]=a1;len=2;
    }
    void outp(){
        printf("{ ");
        for(int i=0;i<len-1;i++){
            printf("%lld, ",a[i]);
        }
        printf("%lld } ",a[len-1]);
    }
};

void mul(const vec& a,const vec& b,vec& c,LLint M){
    c.len=a.len+b.len-1;
    memset(c.a,0x00,sizeof(LLint)*(c.len+1));
    for(int i=0;i<a.len;i++){
        for(int j=0;j<b.len;j++){
            c.a[i+j] = (c.a[i+j] + a.a[i]*b.a[j]%M)%M;
        }
    }
}

void copy(const vec& a,vec& b){
    b.len=a.len;
    memcpy(b.a,a.a,sizeof(LLint)*(a.len+1));
}

int M;
void operator *=(vec& a,const vec& b){
    vec c; mul(a,b,c,M); copy(c,a);
}

LLint fib[maxn]; vec base,step;

int main(){
    freopen("num.in","r",stdin);
    freopen("num.out","w",stdout);
    int k; scanf("%d%lld",&k,&M);
    fib[0]=fib[1]=1;
    for(int i=2; i<=k+1; i++){
        fib[i] = (fib[i-1] + fib[i-2]) %M;
    }
    base.len=base.a[0]=1;
    for(int i=1; i<=k; i++){
        step.set(-fib[i],1);
        base *= step;
    }
    LLint sum=0;
    for(int i=1;i<=k;i++){
        int p; scanf("%d",&p);
        sum = ((sum - base.a[i-1]*p % M)%M + M) %M;
    }
    printf("%lld\n",sum);
    return 0;
}
阅读更多
版权声明:文章纯属版主手敲,请同学们尊重版主的知识产权。 https://blog.csdn.net/GGN_2015/article/details/79349431
文章标签: 数学
个人分类: 算法导论
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

Captain obvious and the Rabbit-Man 谜之数学题

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭