【题解】Another Coin Weighing Puzzle (构造)
或许,这就是神仙题吧
你有(n)包硬币,每包硬币里面都恰好有(k)个硬币。有一包硬币是假的,且假的硬币比真硬币重,且同种硬币重量相同(是个实数)。你可以使用天平称硬币,使用天平的条件是两边有数量相等的硬币。使用天平会返回两边重量之差,测量完之后硬币任你处置。你必须在第一次测量之前确定整个测量的行动策略(但是你可以通过观察每次的结果,把你预设的操作做完后回答假币是哪包),一包硬币中的(k)个硬币外观一样且和别的包的不一样(因此你可以辨认某个硬币是从哪包来的),在你最多使用天平(m)次的前提下,你最多可以在(n)等于多少包中,还能根据你的行动策略确定硬币中假的那一包。答案对(998244353)取模。
硬币哥感觉这题很妙
一包硬币的策略可以看做一个数列。设一个数列({a}) 代表那包假的硬币每一轮放了多少,这个数列长度为(m)且元素的值域为([-k,k]),正的值代表放天平右边,负的值代表放左边,0代表这轮不放
设真硬币重量为(w),假硬币重量为(tw,t>1),第(i)轮的差值为(s_i)
那么
(t,w)是定值,我们可以知道:
(当(s_i=0, ext{then }a_i=0),若(s)全为0那么(a)全为0)
(s)的具体大小对确定(a)是无意义的,这个值可以由(t,w)任意调节。
但可以通过(s)可以得到(a)的比值,此外假若(a)数列所有非零元素的绝对值的(gcd> 1),这个策略和({{a_iover gcd}})是等价的,因此我们规定代表策略的数列的(gcd =1)。
显然,(t,w)不变的情况下,(s)数列和(gcd=1)的数列({a})一一对应。假设总共有(S)种不同的(s),那么我们弄(S)包硬币,且每包硬币分配一个不同的({a}),这样就可以根据(s)直接确定哪一包硬币是假的了,又根据抽屉原理不存在(n>S),而这个构造(n=S)。这么看来,(n=S= ext{count } s= ext{count } a)
那么现在的问题就是( ext{count } a),经典莫比乌斯反演
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e6+5;
const int mod=998244353;
int mu[maxn],pr[maxn],cnt,usd[maxn];
void pre(const int&n){
mu[1]=1;
for(int t=2;t<=n;++t){
if(!usd[t]) mu[t]=mod-1,pr[++cnt]=t;
for(int i=1;i<=cnt;++i){
if(1ll*pr[i]*t>n) break;
usd[pr[i]*t]=1;
if(t%pr[i]) mu[pr[i]*t]=mod-mu[t];
else break;
}
}
}
int ksm(const int&ba,const int&p){
int ret=1;
for(int t=p,b=ba;t;t>>=1,b=1ll*b*b%mod)
if(t&1) ret=1ll*ret*b%mod;
return ret;
}
int main(){
pre(1e6);
int m=qr(),k=qr(),Doinb=1;
for(int t=1;t<=k;++t)
Doinb=(Doinb+1ll*mu[t]*(ksm(k/t*2+1,m)-1))%mod;
cout<<Doinb<<endl;
return 0;
}