Another Coin Weighing Puzzle North America Championship 2020
题意
有称(m)次天平的机会,现有一些盒子,每个盒子中有(k)个硬币
某个盒子中有一个硬币比别的硬币都重,每次称天平可以得到天平的重量差
问可以在(m)次内称出特殊硬币的最大盒子数
[1 leq m,k leq 1e6
]
分析
关键点是得出称量(m)次有效信息是重量差 (感觉也是这题最有思维量的地方),如果特殊硬币比普通硬币重(x),那么(m)次称量可能会得到的答案就是
(-x:x:2x:3x...)
显然,如果得到是(-2x:2x:4x:6x) 这样和上一个称量方法得到信息是重复的(无法确定x)
所以其实就是求(m)次能得到的本质不同(即gcd = 1)的范围在([-k,k])序列比例
这就是和前几天的CF类似的只需要莫比乌斯反演就可以得到的表达式乘上(mu[i])
当然0要特殊考虑
[ans = sum_{i=1}^k mu[i] (2lfloorfrac{k}{d}
floor-1)^m + 1
]
代码
特别注意!mu[i]可能是负数,所以运算要加个MOD
#include<bits/stdc++.h>
#define pii pair<ll,ll>
#define fi first
#define se second
using namespace std;
typedef long long ll;
inline ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int maxn = 2e6 + 5;
const int MOD = 998244353;
inline int mul(int a,int b){
return (ll)a * b % MOD;
}
inline void add(int &a,int b){
a += b;
if(a >= MOD) a -= MOD;
}
inline int ksm(int a,int b = MOD - 2,int m = MOD){
int ans = 1;
int base = a;
while(b){
if(b & 1) ans = mul(ans,base);
base = mul(base,base);
b >>= 1;
}
return ans;
}
int prime[maxn], prime_tot;
int is_prime[maxn];
int mu[maxn];
void pre_calc(int lim) {
mu[1] = 1;
for (int i = 2; i <= lim; i++) {
if (!is_prime[i]) {
prime[++prime_tot] = i;
mu[i] = -1;
}
for (int j = 1; j <= prime_tot; j++) {
if (i * prime[j] > lim) break;
is_prime[i * prime[j]] = 1;
if (i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
else mu[i * prime[j]] = -mu[i];
}
}
}
int main(){
pre_calc(maxn - 3);
int m = rd();
int k = rd();
int ans = 1;
for(int i = 1;i <= k;i++){
if(!mu[i]) continue;
int tmp = ksm(1 + k / i * 2,m);
tmp = MOD + tmp - 1;
tmp %= MOD;
ans += (MOD + (ll)mu[i] * tmp % MOD) % MOD;
ans %= MOD;
}
printf("%d",ans);
}