【每日一题】ABC243G-Sqrt | 数据范围暴力 | 中等

题目内容

原题链接

T T T 组数据,
每组数据给定一个整数 x x x ,构造一个下标从 1 1 1 开始的,长度为 1 0 100 10^{100} 10100 的数组 a a a
满足 a 1 = x a_1=x a1=x a i × a i ≤ a i − 1 ( 2 ≤ i ≤ 1 0 100 ) a_i\times a_i\leq a_{i-1}(2\leq i\leq 10^{100}) ai×aiai1(2i10100)

问可以构造输出多少种不同的数组 a a a

数据范围

1 ≤ x ≤ 9 × 1 0 18 1\leq x\leq 9\times 10^{18} 1x9×1018

题解

一个关键的点在于,后一个数 a i a_i ai 至多是前一个数 a i − 1 a_{i-1} ai1 的平方根,即 a i ≤ a i − 1 a_i\leq \sqrt{a_{i-1}} aiai1

以下用 x \sqrt{x} x 表示数 x x x 开平方根后下取整

所以当每个 a i = a i − 1 a_i=\sqrt{a_{i-1}} ai=ai1
考虑极端情况,
a 1 = 9 × 1 0 18 a_1=9\times 10^{18} a1=9×1018
a 2 = 3 × 1 0 9 a_2=3\times 10^9 a2=3×109
a 3 = 54772 a_3=54772 a3=54772
a 4 = 233 a_4=233 a4=233
a 5 = 15 a_5=15 a5=15
a 6 = 3 a_6=3 a6=3
a 7 = 1 a_7=1 a7=1

故至多 6 6 6 次开根, a i a_i ai 就必须为 1 1 1 了。

定义 f i , j f_{i,j} fi,j 表示数组长度为 i i i ,第 i i i 个数为 j j j 的 方案数

可以知道的是, f 2 , j = 1 f_{2,j}=1 f2,j=1 ,其中 j 2 ≤ x j^2\leq x j2x
那么对于, f 3 , k f_{3,k} f3,k 来说, f 3 , k = ∑ j = k 2 x f 2 , j = x − k 2 + 1 f_{3,k}=\sum\limits_{j={k^2}}^{\sqrt{x}}f_{2,j}=\sqrt{x}-k^2+1 f3,k=j=k2x f2,j=x k2+1

对于 f 4 , f 5 , f 6 , f 7 f_4,f_5,f_6,f_7 f4,f5,f6,f7 来说,取值范围太小,可以直接暴力。

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 55000;
ll dp[N];
ll ndp[N];

void solve() {
    ll a;
    cin >> a;

/*
    dp[0][a] = 1;

    b = sqrtl(a)
    dp[1][b] = 1;
    dp[1][b-1] = 1
    dp[1][b-2] = 1
    dp[1][b-3] = 1
    ...
    dp[1][1] = 1

    c = sqrtl(b)
    dp[2][c] = dp[1][c^2]+dp[1][c^2+1]+...+dp[1][b]
    dp[2][c-1] = dp[1][(c-1)^2]+dp[1][(c-1)^2+1]+...dp[1][c]
*/
    ll b = sqrtl(a);
    ll c = sqrtl(b);


    // dp[2]
    dp[c + 1] = 0;
    for (ll i = c; i >= 1; --i) {
        dp[i] = b - i * i + 1;
        dp[i] += dp[i + 1];
    }

    // dp[3...]
    while (c > 1) {
        ll d = sqrtl(c);

        ndp[d + 1] = 0;
        for (ll i = d; i >= 1; --i) {
            ndp[i] = dp[i * i];
            ndp[i] += ndp[i + 1];
        }

        for (int i = 1; i <= d; ++i) dp[i] = ndp[i];

        c = d;
    }

    cout << dp[1] << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值