题解 [ABC243G] Sqrt
题意
不说,翻译很清楚。
分析
首先看到题面的 $10^{100}$ 次操作,一看就是一个唬人的范围(但是不排除高精度)。
经过观察可以发现,当 $X=10^{18}$ 时,可以发现 $\left\lfloor\sqrt[2^6]X\right\rfloor=1$。
所以说最多添加 $6$ 个数以后就只能添加 $1$ 了,也就意味着此时总方案数不会再变化。
于是我们可以想到动态规划。
状态设计
设 $f_{i,j}$ 为进行 $i$ 次操作,添加的数为 $j$ 时的情况数。
边界条件
$$ f_{0,X}=1 $$
状态设计
$$ f_{i,j}=\sum\limits_{k=j^2}^{\sqrt[2^{i-1}]X}f_{i,k}\quad(1\le j \le \sqrt[2^{i-2}]X) $$
最终答案
$$ f_{6,1} $$
我们可以观察到 $X$ 的范围很大,很难进行存储和转移,所以此时观察规律:$$ \forall 1 \le j \le \left\lfloor\sqrt{X}\right\rfloor,f_{1,j}=1 $$
$$ \forall 1 \le j \le \left\lfloor\sqrt[4]{X}\right\rfloor,f_{2,j}=\sqrt{X}-j^2+1 $$
此时 $\left\lfloor\sqrt[4]{X}\right\rfloor \le 54722$,可以进行存储了。
转移的时候需要后缀和,才能保证在 $O(1)$ 的时间内转移。
时间复杂度 $O(\sqrt[4]X)$。
注意事项
$X \le 9 \times 10^{18}$,需要用 unsigned long long 存储,scanf 的格式符为 %llu,使用流读入的可以跳过。
代码
//the code is from chenjh
#include<cstdio>
#include<cstring>
#include<cmath>
typedef unsigned long long LL;
LL f[11][60000];
LL mysqrt(LL x){//避免浮点误差,进行微调。
LL ret=sqrt(x);
for(;ret*ret<=x;++ret);
for(;ret*ret>x;--ret);
return ret;
}
int main(){
int T;scanf("%d",&T);
for(LL x;T--;){
memset(f,0,sizeof f);//清空数组。
scanf("%llu",&x);
LL sqx=mysqrt(x),ssqx=mysqrt(sqx);
for(int j=1;j<=ssqx;j++) f[2][j]=sqx-((LL)j*j)+1;//预处理 f[2]。
for(int j=ssqx-1;j>0;--j) f[2][j]=f[2][j]+f[2][j+1];//进行后缀和。
for(int i=3;i<=6;i++){
sqx=ssqx,ssqx=mysqrt(ssqx);
for(int j=1;j<=ssqx;j++)f[i][j]=f[i-1][(LL)j*j];
for(int j=ssqx-1;j>0;--j) f[i][j]=(LL)f[i][j]+f[i][j+1];//进行后缀和。
}
printf("%llu\n",f[6][1]);
}
return 0;
}

浙公网安备 33010602011771号