Link
先考虑\(m=2^k\)的情况。
首先我们可以设计一种可行的策略:连续抛\(k\)次硬币得到\(2^k\)种结果,而每个数\(i\)对应其中\(a_i\)种。
不难发现每个数对应的一些结果是可以合并的,即扔不到\(k\)就能确定是哪个数。
可以证明最优的合并方案的结果就是\(\frac{a_i}m\)在\(2\)进制下的各个\(1\)位。
对于每个\(a_i\),设\(\frac{a_i}m=\sum\limits_{x\in S(a_i)}\frac1{2^x}\),那么结果就是\(\sum\limits_{i=1}^n\sum\limits_{x\in S(a_i)}\frac x{2^x}\)。
然后考虑\(m\ne2^k\)的情况。
简单推理可以发现,此时有一些结果不会对应任何一个数,也就是说\(S(x)\)可能变成了一个无限集,但是\(m=2^k\)时的结论依旧适用。
记\(f(\frac xm)=\sum\limits_{i\in S(x)}\frac i{2^i}\),那么答案就是\(\sum\limits_{i=1}^nf(\frac{a_i}m)\)。
但是直接求\(f\)看上去不太现实,我们发现\(f(2x-\lfloor2x\rfloor)=2f(x)-2x\),不难发现它会成环。
成环之后我们就可以推出环中某个\(f(x)\)继而推出环中所有\(f(x)\)了。
#include<stack> #include<cstdio> const int N=10000007,P=998244353; int m,inv,cnt,a[N],pw[N],vis[N],is[N],f[N];std::stack<int>stk; int read(){int x;scanf("%d",&x);return x;} int inc(int a,int b){return a+=b-P,a+(a>>31&P);} int mul(int a,int b){return 1ll*a*b%P;} int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;} int next(int x){return x+x>=m? x+x-m:x+x;} void solve(int x) { int a=1,b=mul(x,inv),y=next(x); while(y^x) b=inc(b,mul(mul(pw[a],y),inv)),++a,y=next(y); f[x]=mul(b,pow(P+1-pw[a],P-2)),is[x]=1; } int main() { int n=read(),ans=0; for(int i=1;i<=n;++i) m+=a[i]=read(); inv=pow(m,P-2),pw[0]=1; for(int i=1,x=P-P/2;i<=m;++i) pw[i]=mul(pw[i-1],x); for(int i=0;i<m;++i) if(!vis[i]) { for(int x=i;!vis[x];x=next(x)) vis[x]=1,stk.push(x); if(!is[next(stk.top())]) solve(stk.top()),stk.pop(); for(int x;!stk.empty();) x=stk.top(),stk.pop(),f[x]=inc(mul(f[next(x)],pw[1]),mul(x,inv)),is[x]=1; } for(int i=1;i<=n;++i) ans=inc(ans,f[a[i]]); printf("%d",ans); }
来源:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12521870.html