题解 [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;
}
posted @ 2023-07-20 15:33  Chen_Jinhui  阅读(14)  评论(0)    收藏  举报  来源