ABC 400 C 2^a a^b 题解

洛谷题面
Atcoder题面
AClink
cnblogs

翻译

当且仅当一个正整数 X 满足以下条件时,它才被称为好整数:

  • 存在一对正整数 (a,b),使得 X=2^a \times b^2

例如,400 是一个好整数,因为 400=2^2 \times 10^2

给定正整数 N,求在 1N 之间(包括收尾)的好整数个数。

思路

很容易想出枚举 a,bO(\log N \times \sqrt{N}) 暴力算法,注意枚举中会出现重复,需要用 map 或 set 记录。

但看到数据范围,很明显会超时,所以思考优化。

首先看能不能优化原式,我们将原式化为 X=2^a \times (2^k \times c)^2,化简得 X=2^{a+2k} \times c^2,其中 c 为奇数。

由这个式子可以得出,枚举的每一个偶数 b 都可以由更大的 a 枚举到的奇数 b 得到,所以我们就不需要枚举偶数 b 了。

但是仅凭优化枚举是不够的,其实上一步已经可以得出能够去掉枚举 b 的循环,我们只需要知道对于现在的 a 能取到的最大的 b 是多少就行了,因为只能取奇数所以这次循环能取的值就是 \frac{b_{max}}{2}+1

对于如何取最大的 b,通过式子推导可得 b \le \left \lfloor \sqrt{\frac{N}{2^a}} \right \rfloor,不过我在调代码时发现这里似乎需要精密处理。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int ans;
signed main(){
	cin>>n;
	for(int a=1;a<=n;a++){//枚举a,这里不用太在意范围,因为后面有退出判断
		int p=n>>a;//相当于n/a^2,位运算优化
		if(!p) break;//a到上限了
		int mxc=sqrt(p);//b
		if(mxc*mxc>p) mxc--;
		while((mxc+1)*(mxc+1)<=p) mxc++;//这两行代码都是上文提到的精密计算
		ans+=(mxc+1)/2;
	}
	cout<<ans;
    return 0;
}
2 个赞

wyd%%%

1 个赞

wyd%%%

像我这种蒟蒻只会写橙题题解

宣:
https://www.luogu.com.cn/problem/solution/P12036
给定一个按特定规则增长的每日收入序列,求当第 a 天收入为 x 时,第 b 天的收入值。规则如下:

  • 1 天收入为 p 摩拉
  • 2 天收入为 p+1 摩拉
  • k\ (k \ge 3) 天收入为前两天的收入之和

若不存在满足条件的 p 或答案不在合法范围内,输出 -1


通过观察发现,每日收入可表示为关于初始值 p 的线性组合。设第 i 天收入为 A_i \cdot p + B_i,其中:

  • A_ip 的系数,满足斐波那契递推关系。
  • B_i 是常数项,也满足斐波那契递推关系。

所以我们可以预处理前 20 天的系数 A_iB_i

根据第 a 天收入 x = A_a \cdot p + B_a 解出 p,验证合法性后代入第 b 天公式。


下面给出正确性证明。

  1. 递推关系正确性
    • 基础情形:第 1、2 天系数正确
    • 归纳假设:假设前 k-1 天成立,则第 k 天满足:
A_k = A_{k-1} + A_{k-2}
B_k = B_{k-1} + B_{k-2}
  1. 解方程正确性
    • 方程 x = A_a p + B_a 的解 p = (x-B_a)/A_a 必须满足:
      • x \ge B_a (非负性)
      • A_a 能整除 (x-B_a)
      • p \in [1, 10^6] (题目约束)

Code

#include <iostream>

using namespace std;

long long A[21], B[21]; // 存储每天的系数和常数项

void pre() {
    A[1] = 1; B[1] = 0;  // 第一天: p
    A[2] = 1; B[2] = 1;  // 第二天: p+1
    for (int i = 3; i <= 20; ++i) {  // 预处理后续天数
        A[i] = A[i-1] + A[i-2];
        B[i] = B[i-1] + B[i-2];
    }
}

int main() {
    pre(); // 预处理系数
    
    int T;
    cin >> T;
    while (T--) {
        int a, b;
        long long x;
        cin >> a >> x >> b;
        
        // 获取第a天的系数和常数项
        long long Aa = A[a], Ba = B[a];
        long long num = x - Ba; // 解方程 Aa*p = x - Ba
        
        // 检查p的合法性
        if (num < 0 || num % Aa != 0) {
            cout << "-1\n";
            continue;
        }
        long long p = num / Aa;
        if (p < 1 || p > 1e6) {
            cout << "-1\n";
            continue;
        }
        
        // 计算第b天收入
        long long Ab = A[b], Bb = B[b];
        cout << Ab * p + Bb << '\n';
    }
    return 0;
}
1 个赞

没事橙题题解也很强了,我之前写的题解也只是黄和绿

我也有绿题的,紫题的,蓝题的(只不过号被封了)

再宣
https://www.luogu.com.cn/article/eyz9bp3c

image
到底是有多么绝望的人才会考出这样的分数

不是不能发AC代码吗??
@稻叶昙 @俞天行

这道题信友队上没有,这是洛谷题解,而且他写在洛谷上明显就是复制过来的,你在cnblogs上看不也一样

只有我一个人的开题顺序是 B->A->E->C->D 吗()

像我这种数学题不到万不得已,坚决不开)

上次打ABC是24年了,不敢这么开