zoukankan      html  css  js  c++  java
  • 测试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. 信息将你我连结。
  • 相关阅读:
    bzoj1015星球大战(并查集+离线)
    bzoj1085骑士精神(搜索)
    bzoj1051受欢迎的牛(Tarjan)
    左偏树学习
    hdu1512 Monkey King(并查集,左偏堆)
    左偏树(模板)
    PAT (Basic Level) Practice (中文) 1079 延迟的回文数 (20分) (大数加法)
    PAT (Basic Level) Practice (中文) 1078 字符串压缩与解压 (20分) (字符转数字——栈存放)
    PAT (Basic Level) Practice (中文) 1077 互评成绩计算 (20分) (四舍五入保留整数)
    PAT (Basic Level) Practice (中文) 1076 Wifi密码 (15分)
  • 原文地址:https://www.cnblogs.com/seamtn/p/11464882.html
Copyright © 2011-2022 走看看