抽卡 状压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\n",f[0][m]); return 0; } /* g++ -std=c++11 1.cpp -o 1 ./1 1 1 50 50 */