Another Coin Weighing Puzzle(莫比乌斯反演,思维)

题目

source

\(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\),这个不同序列和每个硬币包的放置策略是一一对应的,即答案就是满足下列条件的序列数量:

  • \(-k\le 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+2\lfloor\frac{k}{d}\rfloor)^{m}-1)} \]

#include <bits/stdc++.h>

#define endl '\n'
#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;
}
posted @ 2021-08-22 14:46  limil  阅读(128)  评论(0编辑  收藏  举报