题目
有(n)包硬币,其中有一包硬币里全是假硬币。假硬币比真硬币略重,但是不知道真假硬币的确切重量。可以用天平称重(m)次,每次可以往天平两端任意分配硬币包内的硬币。当天平两端硬币数量相等时,就会显示左端重量减去右端重量的值(这个值可正可负数)。
题解
假设真硬盘重(s),假硬币重(s+x)。
假设假硬币在第(i)次称重放了(a_i)个,那么结果就是(a_i x)。(a_i< 0)代表放左边,(a_i > 0)代表放右边。那么根据每次称重的结果序列({a_1x,a_2x,a_3x,...,a_mx})约分后可以得到({a_1,a_2,a_3,...,a_m})。可以知道约分后有(gcd(a_1,a_2,...,a_m)=1),这个不同序列和每个硬币包的放置策略是一一对应的,即答案就是满足下列条件的序列数量:
- (-kle a_i le k)
- (gcd(a_1,...,a_m)=1)或({a_i})均为0。
以样例2 1
为例,满足条件的序列有:
( 0, 0)
( 0, 1)
( 0, -1)
( 1, 0)
( 1, 1)
( 1, -1)
(-1, 0)
(-1, 1)
(-1, -1)
共9种,第一次称重9个硬币包分配方案为({0,0,0,1,1,1,-1,-1,-1}),第二次称重分配方案为({0,1,-1,0,1,-1,0,1,-1})。根据天平返回结果序列约分后查表就可以知道那一包是假硬币。
可以证明这个是最多的(鸽巢原理),而且是合法的(由于对称,每次称重两边放的硬币数一定是相等的)。
使用莫比乌斯反演,容易得到
[ans=1+sum_{d=1}^{k}{mu(d)((1+2lfloorfrac{k}{d}
floor)^{m}-1)}
]
#include <bits/stdc++.h>
#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e6 + 10;
const int M = 998244353;
const double eps = 1e-5;
int isnp[N];
int pri[N];
int mu[N];
int cnt;
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
int main() {
IOS;
isnp[1] = 1;
mu[1] = 1;
for(int i = 2; i < N; i++) {
if(!isnp[i]) {
mu[i] = -1;
pri[cnt++] = i;
}
for(int j = 0; j < cnt && i * pri[j] < N; j++) {
mu[i * pri[j]] = -mu[i];
isnp[i * pri[j]] = 1;
if(i % pri[j] == 0) {
mu[i * pri[j]] = 0;
break;
}
}
}
ll m, k;
cin >> m >> k;
ll ans = 0;
for(int i= 1; i <= k; i++) {
ans = (ans + (qpow(1 + 2 * (k / i), m, M) - 1 + M) * mu[i] % M + M) % M;
}
cout << (ans + 1 + M) % M << endl;
}