hdu 多校状压dp
由于行只有12,所以对行进行状态压缩
0表示这行没有被扎过,也不存在没有爆的气球
1表示这行没有被扎过,存在没有爆的气球
2表示这行被扎过
枚举每一列 对于第i行第j列的气球
2种转移 扎和不扎
扎<---0,1
不扎<---0,1
#include<algorithm> #include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> #include <time.h> #include <vector> #include <bitset> #include <queue> #include <map> #include <set> using namespace std; typedef long long ll; const int N=22,M=531442; const ll outbuf=1000000000000ll; int n,m,q,k,p3[N],num2[M],num1[M],w[M][N]; ll f[N][M],fac[N],Ans[N]; char wq[N][N]; void solve() { scanf("%d%d%d",&n,&m,&q); k=p3[n]; for(int i=0;i<=20;i++) Ans[i]=0; for(int i=1;i<=n;i++) scanf(" %s",wq[i]+1); for(int i=0;i<=m;i++) for(int j=0;j<k;j++) f[i][j]=0; // cout<<k<<endl; f[0][0]=1; for(int i=1;i<=m;i++) { for(int s=0;s<k;s++) { if(f[i-1][s]&&num1[s]<=m-i+1)//s这种状态存在,且没被扎的气球数少于后面可以扎的气球数,每一列至多扎一个 { int nxt=s; for(int j=1;j<=n;j++) if(w[s][j]==0&&wq[j][i]=='Q')//不扎 nxt+=p3[j-1]; f[i][nxt]+=f[i-1][s]; for(int j=1;j<=n;j++) if(wq[j][i]=='Q'&&w[s][j]!=2) { nxt=s+(2-w[s][j])*p3[j-1]//扎 f[i][nxt]+=f[i-1][s]; } } } } for(int s=0;s<k;s++) if(f[m][s]&&num1[s]==0) Ans[num2[s]]+=f[m][s]; for(int i=1;i<=q;i++) { __int128 t=Ans[i];t*=fac[i]; if(t>outbuf) printf("%lld%012lld\n",ll(t/outbuf),ll(t%outbuf)); else printf("%lld\n",ll(t)); // printf("%lld\n",Ans[i]*fac[i]);//,cout<<" "<<Ans[i]<<endl; } // puts(""); } int main() { p3[0]=1; for(int i=1;i<=12;i++) p3[i]=p3[i-1]*3; fac[0]=1; for(int i=1;i<=12;i++) fac[i]=fac[i-1]*i; k=p3[12]; for(int s=0;s<k;s++) { int nm2=0,nm1=0; for(int i=1;i<=12;i++) { w[s][i]=(s/p3[i-1]%3); if(w[s][i]==1) nm1++; else if(w[s][i]==2) nm2++; } num2[s]=nm2;num1[s]=nm1; } int t;cin>>t; while(t--) solve(); // cout<<clock()<<endl; return 0; }