• 测试35:抽卡


    状态设计:成环使首尾相连
    为何连成环?
    单纯链式地跑,状态是无限的。
    但是因为状态是首位相接的,所以可以连成环,在环上可以无限地跑
    最终状态是f(0,m),而不是f(0,0).(0,m)没开始,(0,0)已开始。
    处理环:1、高斯消元。但是是取模意义下的,而且复杂度太大会T
        2、系数递推。相当于手动高斯了。因为to(i,j)一定比当前多,可以先处理出来,就成了常量。
        而环的问题可以写出a*f0+b=fm,fm=f0+1;手动解。
        可行性在于每个状态转移来的未知量只有一个。
    to(i,j)函数:s+(1<<x),而不是((s>>x)+1)<<x,这会消去后面的x位1;
    写时仔细想想。写完把函数逐一测一测。

    #include<bits/stdc++.h>
    #define F(i,a,b) for(rg int i=a;i<=b;++i)
    #define rg register 
    #define LL long long
    #define il inline
    #define pf(a) printf("%lld ",a)
    #define phn puts("")
    using namespace std;
    #define int LL
    int read();
    /*
    状态设计:成环使首尾相连
    为何连成环?
    单纯链式地跑,状态是无限的。
    但是因为状态是首位相接的,所以可以连成环,在环上可以无限地跑
    最终状态是f(0,m),而不是f(0,0).(0,m)没开始,(0,0)已开始。
    处理环:1、高斯消元。但是是取模意义下的,而且复杂度太大会T
        2、系数递推。相当于手动高斯了。因为to(i,j)一定比当前多,可以先处理出来,就成了常量。
        而环的问题可以写出a*f0+b=fm,fm=f0+1;手动解。
        可行性在于每个状态转移来的未知量只有一个。
    to(i,j)函数:s+(1<<x),而不是((s>>x)+1)<<x,这会消去后面的x位1;
    写时仔细想想。写完把函数逐一测一测。
    */
    int n,m;
    int p[30],q[30];
    int f[420010][70];
    const int mod=2000000011,inv3=1333333341;
    il int qpow(int x,int k){int s=1;for(;k;k>>=1,x=x*x%mod)if(k&1)s=s*x%mod;return s;}
    il int cal(int s,int i){
        return ((s>>((i-1)<<1))&3)*1333333341%mod;
    }
    il int to(int s,int i){
        return s+(1<<((i-1)<<1));
    }
    int a[70],b[70];
    il int MO(int x){return x<mod?x:x-mod;}
    signed main(){
    //    freopen("ex_card2.in","r",stdin);
        n=read();m=read();
        p[0]=q[0]=100*n;int bas=qpow(100*n,mod-2);
        F(i,1,n)p[0]-=(p[i]=read()),p[i]=p[i]*bas%mod;
        F(i,1,n)q[0]-=(q[i]=read()),q[i]=q[i]*bas%mod;
        p[0]=p[0]*bas%mod;q[0]=q[0]*bas%mod;
        int mx=(1<<n*2)-1;
        for(rg int s=mx-1;~s;--s){
            F(k,0,m)a[k]=p[0],b[k]=0;
            a[m-1]=q[0];a[m]=0; 
            F(i,1,n){
                a[m-1]=MO(a[m-1]+q[i]*cal(s,i)%mod);
                if(1-cal(s,i))
                b[m-1]=MO(b[m-1]+q[i]*(1-cal(s,i)+mod)%mod*f[to(s,i)][m]%mod);
            }
            for(rg int k=m-2;k>=0;--k){
                F(i,1,n){
                    a[k]=MO(a[k]+p[i]*cal(s,i)%mod);
                    if(1-cal(s,i))
                    b[k]=MO(b[k]+p[i]*(1-cal(s,i)+mod)%mod*f[to(s,i)][k+1]%mod);
                }
                b[k]=MO(a[k]*b[k+1]%mod+b[k]);
                a[k]=a[k]*a[k+1]%mod;
            }
            f[s][m]=(b[0]+1)*qpow((1ll-a[0]+mod)%mod,mod-2)%mod;
            for(rg int k=m-1;k>=0;--k){
                f[s][k]=MO(a[k]*f[s][m]%mod+b[k]);
            }
        }
    //    pf(f[6]);
        printf("%lld
    ",f[0][m]);
    }
    il int read(){
        int s=0;char ch;
        while(ch=getchar(),!isdigit(ch));
        for(;isdigit(ch);s=s*10+(ch^48),ch=getchar());
        return s;
    }
    /*
    g++ 1.cpp -g
    time ./a.out
    1 1
    27 74
    
    2 1
    84 84
    54 54
    
    1 1
    27 74
    */
    /*
    int pc=q[0],s=1;
            F(j,1,n){
                pc=(pc+q[j]*cal(i,j)%mod)%mod;
                s=(s+(1ll-cal(i,j)+mod)*q[j]%mod*f[to(i,j)]%mod)%mod;        
            }
            f[i]=s*qpow((1ll-pc+mod)%mod,mod-2)%mod;
        //    pf(i);pf(f[i]);phn;
    */
    View Code

    题解:

    对于一般情况,不同种类的卡之间概率不等,不再等价,但是抽到每种颜
    色的概率都相等,所以对于每种卡来说不同颜色之间是等价的,只需记录每种
    颜色当前已经抽出了多少张卡(0 ∼ 3 张),于是可以用一个 n 位的四进制数来
    记录,状态数为 4 n 。
    记 f [s][k] 表示当前的四进制数状态为 s,本次氪金已经抽了 k 次,到结束
    时的期望氪金次数。当 s 已经包含了所有颜色的所有种类的卡时,f [s][k] = 0;
    否则,当 k = m 时,因为一次氪金抽卡的结束后是另一次氪金抽卡的开始,所
    以 f [s][m] = f [s][0] + 1;否则,记 c(s, i) 表示状态 s 中第 i 种卡片出现了多少
    种颜色,则 k < m − 1 时(其中 t i 表示)
    (
    (
    )
    )
    n

    c(s, i)
    c(s, i)
    f [s][k] =
    p i
    f [s][k + 1] + 1 −
    f [t i ][k + 1]
    3
    3
    i=1
    k = m − 1 时将 p i 换成 q i 即可。最后答案为 f [0][m](注意 f [0][0] 表示的是一
    次氪金抽卡已经开始,f [0][m] 才表示还没有开始)。因为 s 相同时转移会成环,
    即 f [s][k] 要用到 f [s][k + 1],而 f [s][m] 要用到 f [s][0],可以用高斯消元对这
    m + 1 个变量解方程组,时间复杂度为 Θ(4 n (mn + m 3 ))。

    然后将 f [s][m − 1] 代入 f [s][m − 2],可以得到
    f [s][m − 2] = c m−2 (a m−1 f [s][m] + b m−1 ) + d m−2
    = c m−2 a m−1 f [s][m] + c m−2 b m−1 + d m−2
    = a m−2 f [s][m − 1] + b m−2
    也即
    {
    a m−2 = c m−2 a m−1
    b m−2 = c m−2 b m−1 + d m−2
    按 k 从大到小的顺序,可以依次求出 a k , b k ,即将 f [s][k] 全部表示成 f [s][k] =
    a k f [s][m] + b k 的形式,最后得到
    f [s][0] = a 0 f [s][m] + b 0

    f [s][m] = f [s][0] + 1
    联立两方程可以解出 f [s][m] 的值,然后再代入 f [s][k] = a k f [s][k] + b k 求出所
    有 f [s][k]。这样避免了高斯消元,时间复杂度为 Θ(4 n mn)。

    Informatik verbindet dich und mich. 信息将你我连结。
  • 相关阅读:
    审核系统
    ehcache 缓存
    tomcat 内存设置
    html5 开发 跨平台 桌面应用
    service thread 结合使用
    html5桌面应用
    鼠标 事件
    服务器 判断 客户端 文件下载
    使用github管理Eclipse分布式项目开发
    uub代码
  • 原文地址:https://www.cnblogs.com/seamtn/p/11464882.html
Copyright © 2020-2023  润新知