小Z的礼物
小Z有一个神奇的自动售货机,里面有 (n imes m) 种物品,分别放在 (n) 行 (m) 列个格子中。每当小Z向自动售货机中投入一枚硬币,他就能获得一对相邻格子中的物品(已经获得的物品可能再次获得),获得每一对相邻格子中的物品的概率是相等的。在这 (n imes m) 种物品中,有一些物品是小Z喜欢的(小Z喜欢的用 * [星号] 表示,其他的用 .[英文句号] 表示),他想把这些物品包装成一份礼物。小Z想知道,期望投入多少枚硬币后,就可以获得这些他喜欢的物品。
对于所有数据,保证 (1 leq n leq 6, 2 leq m leq 100)。
题解
一眼就能看出是min-max容斥。
其中 (all=n(m-1)+m(n-1)) 表示多米诺骨牌总数。(F(T)) 表示能覆盖 (T) 中至少一个点的多米诺骨牌数量。
考虑设计DP求出所有 (F(T)=x) 的 (T) 的容斥系数 ((-1)^{|T|+1}) 和。向 (T) 中加入一个星号时可以向四周摆放多米诺骨牌,两个相邻的星号会算重,所以我们需要用状压记录选择情况。
设 (dp(i,j,S,T,x)) 表示做到格子 ((i,j)),前一列的星号选择情况为 (S),这一列 (1sim i-1) 行的星号选择情况为 (T),多米诺骨牌总数为 (x) 的容斥系数和。显然 (O(2^{2n}n^2m^2)) 会TLE。
注意到我们只需要知道前一列 (isim n) 行的星号选择情况,所以可以使用轮廓线优化。时间复杂度 (O(2^nn^2m^2))。
min-max容斥的时候不需要管 (T) 集合以外的元素的情况。这点很显然,但是我之前没理解透,看了好久那个
dx
的转移百思不得其解……
CO int N=6,M=101;
char a[N][M];
int dp[2][1<<N][2*N*M];
int main(){
int n=read<int>(),m=read<int>();
for(int i=0;i<n;++i) scanf("%s",a[i]);
int sum=n*(m-1)+m*(n-1);
int now=0,pre=1;
dp[now][0][0]=mod-1;
for(int j=0;j<m;++j)for(int i=0;i<n;++i){
swap(now,pre),memset(dp[now],0,sizeof dp[now]);
for(int s=0;s<1<<n;++s)
for(int x=0;x<=sum;++x)if(dp[pre][s][x]){
int t=s>>i&1?s^1<<i:s;
cadd(dp[now][t][x],dp[pre][s][x]);
if(a[i][j]=='.') continue;
t|=1<<i;
int dx=(i>0 and ~s>>(i-1)&1)+(j>0 and ~s>>i&1)+(i<n-1)+(j<m-1);
cadd(dp[now][t][x+dx],mod-dp[pre][s][x]);
}
}
int ans=0;
for(int i=1;i<=sum;++i){
int inv=fpow(i,mod-2);
for(int s=0;s<1<<n;++s) cadd(ans,mul(dp[now][s][i],inv));
}
ans=mul(ans,sum);
printf("%d
",ans);
return 0;
}
轮廓线DP常用优化办法:滚动数组。它的意义不仅在于卡空间,而且能方便转移。比如列数变化的时候。