BZOJ1879_Bill的挑战_KEY

题目传送门

第一次看题目感觉毫无还手之力,一看M的范围≤15,果断状压。

但是状压的想法比较新奇。

先想到的状压是设f[i][j]表示前i个状态为j时的方案总数,但是后来想了一想不行,会超时。

于是以f[i][j]表示i状态匹配到第j位时的方案总数。

但判断转移是会超时,于是预处理串与串之间的关系。

设g[i][j]表示第i位(字符串)为字母j时有多少串符合。

f转移时直接&g数组就好了。

code:

/**************************************************************
    Problem: 1879
    User: yekehe
    Language: C++
    Result: Accepted
    Time:804 ms
    Memory:7820 kb
****************************************************************/
 
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
 
const int mod=1000003;
 
int T,N,K,f[1<<15][51],g[51][26];
string S[55];
 
int main()
{
//  freopen("x.txt","r",stdin);
    scanf("%d",&T);
    register int i,j,k;
        while(T--){
            scanf("%d%d\n",&N,&K);
            memset(f,0,sizeof f);
            memset(g,0,sizeof g);
                for(i=1;i<=N;i++)cin>>S[i];
            int len=S[1].size();
                for(i=0;i<len;i++){
                    for(j=0;j<26;j++)
                        for(k=1;k<=N;k++)
                            if(S[k][i]=='?' || S[k][i]==(j+'a'))
                                g[i][j]|=1<<k-1; 
                }
            f[(1<<N)-1][0]=1;
                for(i=0;i<len;i++){
                    for(j=0;j<1<<N;j++){
                        if(f[j][i])//这里注意要判断,不然会因为%的常数太大而超时。
                            for(k=0;k<26;k++){
                                f[j&g[i][k]][i+1]=(f[j&g[i][k]][i+1]+f[j][i])%mod;
                            }
                    }
                }
            int ans=0;
                for(i=0;i<1<<N;i++){
                    j=i,k=0;
                        while(j){k+=j&1;j>>=1;}
                    if(k==K)ans=(ans+f[i][len])%mod;
                }
            printf("%d\n",ans);
        }
    return 0;
}

 

posted @ 2018-01-29 15:43  Cptraser  阅读(112)  评论(0编辑  收藏  举报