• 抽卡 状压DP+期望DP+系数递推


      这题的从状态定义上就有点特别,考场上没想到怎么设定状态来限制1次克金中m次的抽取。只打了m=1的点,没取模爆0了。加上是20分。

      我的状态定义很简单,但是这个题如果把每个物品拆成三个然后硬转移状态数太多,这样的话一共有27个物品,显然1e9就死了。

      那么考虑为什么要状压,其实是因为不同种物品概率不同,并不等价,所以我们要进行转移是要知道该乘谁的概率,那么对于每种物品的每种颜色来说,这概率都相等,就直接记录个数就行了,最多有3个,正好0 1 2 3是4个状态,一种物品4个状态,4进制刚刚好。状态数26万多可过。(插头DP打下的基础。。。。)

      我的定义是每个物品数状态为s的期望购买次数为$f[s]$,那么就可以直接转移了,$f[s]=sum limits_{i=1}^{n} q[i]*(sum/3*f[s][i]+(3-sum(s,i))/3*f[add(s,i)]+1$add为在s状态上把第i个物品数加1的新状态,但是如果已经为3那么新状态不再变化,化简一下转移方程,自己从自己转移过来,自环很好处理,手解方程即可。记录一下系数和常数直接作除就行了。

      然后就考虑怎么拿别的分数,发现我的这个转移它一次购买能抽很多次,然后状态就能从很多很多转移过来,需要列很多很多,而且概率也有很多很多,貌似是dfs才能处理的那种,就是枚举每一次抽到什么。

      这种时候就要考虑原来把dfs搞成DP,现在也得把这个dfs搞成DP,那么需要加一维。

      设$f[s][k]$为已有状态为s,此时本次购买已经抽了k次的期望购买次数,这种求期望次数的DP,一定不能把次数放到状态里,因为这个东西有可能是无限次,概率很小,然后贡献答案。那么转移是很显然的。首先$f[满状态][k]=0$

      $f[s][k]=sum limits_{i=1}^{n} p[i]*(sum/3*f[s][k+1]+(3-sum)/3*f[add(s,i)][k+1])$

      当$f[s][m]$进行转移时表明状态s已经对其他次数做了贡献,留下来的要往后接着转移就是$f[s][0]=f[s][m]+1$

      这玩意有后效性,但如果把$f[s]$的转移关系列出来只是一个环,这就好办了。

      我们的方法是:系数递推。这是个套路化的化简方法,应用于单环的方程组线性求解,先体会一下过程。以下省略第一维状态

      设$f[k]=a[k]*f[m]+b[k]$

      然后有$f[k-1]=t*f[k]+d$这些系数显然都是可以求的。

      那么$f[k-1]=t*(a[k]*f[m]+b[k])+d$

      $f[k-1]=t*a[k]*f[m]+t*b[k]+d$

      因为$f[k-1]=a[k-1]*f[m]+b[k-1]$

      然后发现就得到了$a[k-1]=t*a[k]$  $b[k-1]=t*b[k]+d$

      在逆推的情况下系数可以直接得到,

      那么我们这么做的目的就出来了,要得到$f[0]=a[0]*f[m]+b[0]$

      然后$f[0]=f[m]+1$接下来不用交给高斯了,交给手就行了

      推出f[m]以后再用$f[k]=a[k]*f[m]+b[k]$推一遍。

      所以系数递推基本思路就是,如果每个方程都能写成一个单环的递推,设0为起始点,通过一条0到m的边得到一个方程,m绕一圈到0的边通过迭代递推的到m到0的方程,交给手解出来。

      

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=9,M=65,mod=2000000011;
    long long f[1<<(N<<1)][M],a[M],b[M],p[N],q[N],pi[N],mac[N*2+1];
    inline long long rd()
    {
        long long s=0,w=1;
        char cc=getchar();
        for(;cc<'0'||cc>'9';cc=getchar()) if(cc=='-') w=-1;
        for(;cc>='0'&&cc<='9';cc=getchar()) s=(s<<3)+(s<<1)+cc-'0';
        return s*w;
    }
    inline long long qpow(long long a,long long k)
    {
        long long ans=1;
        for(;k;k>>=1,a=1ll*a*a%mod) if(k&1) ans=1ll*a*ans%mod;
        return ans;
    }
    inline long long inv(long long a) {return qpow(a,mod-2);}
    inline int find(int s,int j){return (s>>((j-1)<<1))&3;}
    inline int add(int s,int j){if(find(s,j)<3) s+=(1<<((j-1)<<1));return s;}
    int main()
    {
        long long n=rd(),m=rd(),sq=1,sp=1,inv_3=inv(3);a[m]=1;b[m]=0;
        for(int i=1;i<=n;i++) p[i]=rd()*inv(100*n)%mod,sp=(sp-p[i]+mod)%mod;
        for(int i=1;i<=n;i++) q[i]=rd()*inv(100*n)%mod,sq=(sq-q[i]+mod)%mod;
        for(int s=(1<<(n<<1))-2;s>=0;s--)
        {
            for(int k=m-1;k>=0;k--)
            {
                long long t=(k+1==m)?sq:sp,d=0;
                for(int i=1;i<=n;i++)
                {
                    long long pi=(k+1==m)?q[i]:p[i];
                    t=(t+pi*find(s,i)%mod*inv_3%mod)%mod;
                    d=(d+pi*(3ll-find(s,i)+mod)%mod*inv_3%mod*f[add(s,i)][k+1]%mod)%mod;
                }
                a[k]=t*a[k+1]%mod;b[k]=(t*b[k+1]%mod+d)%mod;
            }
            f[s][m]=(1ll+b[0]+mod)%mod*inv((1ll-a[0]+mod)%mod)%mod;
            for(int k=m-1;k>=0;k--)f[s][k]=(a[k]*f[s][m]%mod+b[k])%mod;
        }
        printf("%lld
    ",f[0][m]);
        return 0;
    }
    /*
    g++ -std=c++11 1.cpp -o 1
    ./1
    1 1
    50
    50
    */
    View Code
  • 相关阅读:
    在WINDOWS任务计划程序下执行PHP文件 PHP定时功能的实现
    使用Sublime Text 3进行Markdown 编辑+实时预览
    ni_set()函数的使用 以及 post_max_size,upload_max_filesize的修改方法
    CORS跨域的概念与TP5的解决方案
    tp5模型笔记---多对多
    微信小程序 GMT+0800 (中国标准时间) WXSS 文件编译错误
    ESP8266 LUA脚本语言开发: 外设篇-GPIO输入检测
    ESP8266 LUA脚本语言开发: 外设篇-GPIO输出高低电平
    ESP8266 LUA脚本语言开发: 准备工作-LUA文件加载与变量调用
    ESP8266 LUA脚本语言开发: 准备工作-LUA开发说明
  • 原文地址:https://www.cnblogs.com/starsing/p/11465138.html
Copyright © 2020-2023  润新知