『题解』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;
}