其他分享
首页 > 其他分享> > $[SDOI2009]Bill$的挑战

$[SDOI2009]Bill$的挑战

作者:互联网

\([SDOI2009]Bill\)的挑战

观察数据范围,显然是状压。

但是如果你将\(K\)加进状态中,手推一下就会发现这里要用到容斥。

但我又不是讲容斥的是吧。。。

所以我们尝试不将\(K\)加入状态中,而是在最后枚举恰好含有\(K\)个元素的子集个数。

我们设\(f[i][j]\)表示对于所有集合\(i\)中的元素,匹配到第\(j\)位时的方案数。

实际上我们涉及到集合的状压转移时如果考虑一个状态由哪里转移来,要枚举合法子集,显然麻烦。

我们可以考虑当前状态可转移到哪里,这样只用造出合法状态即可。

我们同时还要预处理一个\(Mat[i][j]\)表示一个集合,其中的所有元素满足,第\(i\)位可以匹配到字母\(j\)。

有点卡时限,最好不要用\(memset\)。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int N=17;
const int M=60;
const int p=1e6+3;
char S[N][M];
int n,m,K,ans;
int f[1<<N][M],Mat[M][M];
signed main(){
#ifndef ONLINE_JUDGE
    freopen("A.in","r",stdin);
#endif
    int T=read();
    while(T--)
    {
        n=read(),K=read();
        for(register int i=1;i<=n;i++) scanf("%s",S[i]);
        m=strlen(S[1]);ans=0;
        for(register int i=0;i<m;i++)
            for(register int C=0;C<26;C++)
                for(register int j=1;j<=n;j++)
                    if(S[j][i]=='?'||S[j][i]==C+'a')
                        Mat[i][C]|=(1<<(j-1));
        f[(1<<n)-1][0]=1;
        for(register int i=0;i<m;i++)
            for(register int j=0;j<=(1<<n)-1;j++)
                if(f[j][i]) 
                    for(register int C=0;C<26;C++)
                        f[Mat[i][C]&j][i+1]=(f[Mat[i][C]&j][i+1]+f[j][i])%p;
        for(register int i=0;i<=(1<<n)-1;i++)
        {
            register int Sum=0;
            for(register int j=1;j<=(1<<n)-1;j<<=1) Sum+=(bool)(i&j);
            if(Sum==K) ans=(ans+f[i][m])%p;
        }
        printf("%d\n",ans);
        for(register int i=0;i<=(1<<n);i++)
            for(register int j=0;j<=m;j++) f[i][j]=0;
        for(register int i=0;i<=m;i++)
            for(register int C=0;C<=26;C++) Mat[i][C]=0;
    }
}

标签:状态,状压,int,挑战,Bill,while,&&,SDOI2009
来源: https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11741891.html