title: 组合数学与计数原理
tags: []
categories: [OI, 算法专题]

N中的元素 X中的元素 f任意 f单射 f满射
可区分 可区分 \(x^n\)
不可区分 可区分 \(\binom{n}{x+n-1}\)
可区分 不可区分
不可区分 不可区分

P9118 春季测试 2023 幂次

原题

题意

  • 求 \(1 \sim n\) 中有多少正整数可以表示为 \(a^b\) 的形式,其中 \(a, b\) 为正整数且 \(b \ge k\)。同一个数的多种表示只计一次。

数据范围:\(1 \le n \le 10^{18}\),\(1 \le k \le 100\)。

思路

有点人类智慧的一个题。
k=1时显然答案是n。
k>2时答案很好统计,因为1e18的sqrt3只有1e6个,枚举然后再while指数就可以了。
当指数是2时,显然是sqrt2。但是有的答案在指数>2时也被统计了,就需要减去重复的部分。做法是在循环中如果是完全平方的数就统计,最后减去就好了。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define Dl(a) cout << #a << " : " << a << "\n";
#define D(a) cout << #a << " : " << a;
#define Da(a,i,j) cout << #a << " : ";f(idx,i,j){cout << a[idx] <<" ";}
int n,k;
map <int,bool> mp;
int pow3,pow2AndPow3;
void solve(){
	for (int i=2;i*i*i<=n;i++){
//		Dl(i);
		int d = i * i,p = 2;
		while (d<= n / i){
			p ++;
			d *= i;
			if (mp[d]) continue;
			if (p < k) continue;
			mp[d] = 1,pow3 ++;
			if ((int)sqrtl(d) * (int)sqrtl(d) == d){
//				Dl(sqrtl(d));
//				Dl(d);
				pow2AndPow3++;
			}
//			D(d);Dl(p);
		}
	}
}
signed main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	cin >> n >> k;
	if (k == 1) cout << n;
	else if (k >= 3){
		solve();
		cout << pow3 + 1;
	}
	else{
		solve();
//		Dl(pow3);
//		Dl(pow2AndPow3);
		cout << (long long)sqrtl(n) + pow3 - pow2AndPow3;
	}
	return 0;
}