题解 [SDOI2009]Bill的挑战 容斥

题解 [SDOI2009]Bill的挑战

题目描述

n n n个字符串,每个字符串由a~z或者?组成,其中?可以匹配任意字符。

现在问有多少个串 T T T,满足能够恰好与 n n n个串中的 k k k个匹配。

n ≤ 20 , n \leq 20, n20,字符串长度 ≤ 50 \leq 50 50

具体做法

这是一道容斥题,当然一看数据范围也可以用状压 d p dp dp写。

考虑我们能够预处理出什么,数据这么小那么我们可以爆搜处理出 T T T至少匹配了 i i i个串的总数,记为 t o t i tot_i toti

(暴力枚举选择情况,再来匹配)。

那么题目要求的恰好匹配 k k k个就可以用总的减去多选的。

a n s i ans_i ansi表示从 n n n个中恰好匹配 i i i个的方案,现在要求 a n s j , j < i ans_j,j < i ansj,j<i

首先我们把 a n s j ans_j ansj加上 t o t j tot_j totj。那么现在多算了实际上一定能够匹配 i i i个串,但只选了 j j j个串来匹配的方案。

对于能够匹配 i i i个串的方案,我们若去掉任意 i − j i-j ij个串,那么就能够成为一个非法的恰好只匹配 j j j个串的方案,从中去掉 i − j i-j ij个串有 C i i − j C_{i}^{i-j} Ciij个方案。

所以有 a n s j = t o t j − ∑ i = j + 1 n ( i i − j ) a n s i ans_j=tot_j-\sum_{i=j+1}^{n}(_i^{i-j})ans_i ansj=totji=j+1n(iij)ansi

C o d e \mathcal{Code} Code

/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月27日 星期日 20时28分52秒
*******************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define int long long 

using namespace std;

const int maxn=55;
const int mod=1000003;
int n,k,len,stk[maxn],_top,num,tot,c[maxn][maxn],ans[maxn];
string a[maxn];

void dfs(int now,int chos)
{
    if(now==n+1)
    {
        if(chos!=num) return;
        long long res=1;
        for(int i=1;i<=len;i++)
        {
            int ch=-1;
            for(int j=1;j<=num;j++)
            {
                if(a[stk[j]][i]!='?')
                {
                    if(ch==-1)
                        ch=a[stk[j]][i]-'a';
                    else if(ch!=a[stk[j]][i]-'a')
                        return;
                }
            }
            if(ch==-1)
                res=res*26LL%mod;
        }
        tot=(tot+res)%mod;
        return;
    }
    if(chos<num)
    {
        stk[++_top]=now;
        dfs(now+1,chos+1);
        stk[_top--]=0;
    }
    if(n-now>=num-chos)
        dfs(now+1,chos);
}

inline void init(int n=50)
{
    for(int i=0;i<=n;i++)
        c[i][i]=c[i][0]=1;
    for(int i=2;i<=n;i++)
        for(int j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}

signed main()
{
    //freopen("p2167.in","r",stdin);
    //freopen("p2167.out","w",stdout);
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    init();
    while(T--)
    {
        cin>>n>>k;
        for(int i=1;i<=n;i++)
            cin>>a[i],a[i]="$"+a[i];
        len=a[1].size()-1;
        for(int i=n;i>=k;i--)
        {
            tot=0,num=i; _top=0;
            dfs(1,0);
            long long res=0;
            for(int j=i+1;j<=n;j++)
                res=(res+1ll*c[j][i]*ans[j])%mod;
            ans[i]=(tot-res+mod)%mod;
        }
        cout<<ans[k]<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值