『题解』Luogu P8377 [PFOI Round1] 暴龙的火锅

题目传送门

题目大意

定义 \(S(x)\)\(x\) 的每一位数字之和,例如:\(S(14)=1+4=5\)\(S(114514)=1+1+4+5+1+4=16\)

定义 \(fib(x)\) 为斐波那契数列第 \(x\) 项,具体地:

\[fib(x)=\begin{cases} 1&x \le 2\\ fib(x-1)+fib(x-2)&x \ge 3\\\end{cases} \]

现给定 \(n\),求下式的值:

\[(S(fib(1))+S(fib(2))+S(fib(3))+\dots+S(fib(n)))\bmod9 \]

多组数据。

思路

写个暴力的话,查询 \(\mathcal{O}(n)\),可得 \(40\:\texttt{pts}\)

但我们注意到求值的式子,显然可以事先预处理出所有的 \((S(fib(1))+S(fib(2))+\dots+S(fib(i)))\bmod9\),查询达到 \(\mathcal{O}(1)\)

先一遍 \(\mathcal{O}(n)\) 处理出所有 \(fib(i)\bmod9\),再一遍 \(\mathcal{O}(n)\) 处理 \(S(fib(i))\) 的前缀和,前缀和数组 \(ans_i\) 等于 \((S(fib(1))+S(fib(2))+\dots+S(fib(i)))\bmod9\),查询时直接输出 \(ans_n\) 即可。

处理前缀和时,\(ans_{i-1}\) 就是 \((S(fib(1))+S(fib(2))+\dots+S(fib(i-1)))\bmod9\),所以再加上 \(S(fib(i))\) 就是答案了。

代码

#include <iostream>
using namespace std;
template<typename T=int>
inline T read(){
    T X=0; bool flag=1; char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<1)+(X<<3)+ch-'0',ch=getchar();
    if(flag) return X;
    return ~(X-1);
}

template<typename T=int>
inline void write(T X){
    if(X<0) putchar('-'),X=~(X-1);
    T s[20],top=0;
    while(X) s[++top]=X%10,X/=10;
    if(!top) s[++top]=0;
    while(top) putchar(s[top--]+'0');
    putchar('\n');
}

const int N=1e6+5,mod=9;
int t,n;
int fib[N],ans[N];

int s(int x){ // 题目中的 S(x)
    int res=0;
    while(x){
        res+=x%10;
        x/=10;
    }
    return res;
}

int main(){
    t=read();
    // 初始化
    fib[1]=fib[2]=1;
    n=1e6; // 只是方便预处理使用
    // 先算出 fib[i]
    for(int i=3; i<=n; i++) fib[i]=(fib[i-1]+fib[i-2])%mod;
    // 再进行前缀和
    for(int i=1; i<=n; i++) ans[i]=(ans[i-1]+s(fib[i]))%mod;
    // O(1) 查询
    while(t--) write(ans[read()]);
    return 0;
}
posted @ 2022-06-05 11:07  仙山有茗  阅读(48)  评论(0编辑  收藏  举报