LOJ6267 生成随机数

一曲冷凌霜 提交于 2020-03-19 08:17:40

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);
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!