zoukankan      html  css  js  c++  java
  • 【[SDOI2009]Bill的挑战】

    一看题解好像全是状压DP,那么我就来补充一个容斥写法吧

    乍一看,好像是水题,枚举选哪k个串,然后判断

    1,如果这k个串中至少两个串某位置确定且不相同,答案显然为0
    2,如果这个位置只被有且仅有一个串确定,这个位置就唯一确定了
    3,否则这个位置有26种不同填数情况,统计答案时只要用乘法原理搞一下就行

    但是容易想到,这样做是有问题的,以样例的第一组数据为例
    我们选定串1,2,然后发现第四个位置确定是r,其他位置任选,但是无论我们构造出怎样的串,T总是可以同时匹配串3的

    考虑容斥掉这些匹配到更多串的方案

    首先,我们可以用上述方法求出匹配至少i个串的方案数,记为num[i]
    我们需要统计恰好满足匹配i个的情况,记为ans[i]
    现在问题来了,怎么容斥

    考虑ans[i]与ans[j]的联系(i>j),定义保证ans[j]是恰好匹配j个串
    如果再匹配到i-j个串,就是ans[j]
    在i个串中,这i-j个串的选择当然就有C(i,i-j)种方案
    我们有num[j],得出公式ans[j]=num[j]-∑C(i,i-j)*ans[i]
    倒序处理ans数组即可

    上代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int mod=1000003;
    int t,n,k,num[20],len,c[20][20],ans[20];
    char s[20][60];
    int ksm(int x,int t)
    {
        int ret=1;
        while(t)
        {
            if(t&1)
                ret=(ll)ret*x%mod;
            x=(ll)x*x%mod,t>>=1;
        }
        return ret%mod;
    }
    int check(int x)
    {
        char tmp[60];
        for(int i=0;i<len;i++)
            tmp[i]='?';
        int rest=len;
        for(int i=0;i<n;i++)
        {
            if(x&(1<<i))
                for(int j=0;j<len;j++)
                    if(!isalpha(tmp[j])&&isalpha(s[i+1][j]))
                    {
                        tmp[j]=s[i+1][j];
                        rest--;
                    }
                    else if(isalpha(tmp[j])&&isalpha(s[i+1][j])&&tmp[j]!=s[i+1][j])
                        return 0;
        }
        return ksm(26,rest);
    }
    int main()
    {
        for(int i=0;i<20;i++){
            c[i][0]=1;
            for(int j=1;j<=i;j++){
                c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
            }
        }
        scanf("%d",&t);
        while(t--)
        {
            memset(num,0,sizeof(num));
            scanf("%d%d",&n,&k);
            for(int i=1;i<=n;i++)
                scanf("%s",s[i]);
            len=strlen(s[1]);
            int mul=1;
            if(k>n)
            {
                printf("0
    ");
                continue;
            }
            for(int i=1;i<(1<<n);i++)
            {
                int cnt=0;
                for(int j=0;j<n;j++)
                    if(i&(1<<j))
                        cnt++;
                if(cnt<k)
                    continue;
                (num[cnt]+=check(i))%=mod;
            }
            for(int i=n;i>=k;i--)
            {
                int sum=0;
                for(int j=i+1;j<=n;j++)
                    (sum+=(ll)c[j][i]*ans[j]%mod)%=mod;
                ans[i]=((num[i]-sum)%mod+mod)%mod;
            }
            printf("%d
    ",(ans[k]%mod+mod)%mod);
        }
        return 0;
    }
  • 相关阅读:
    Linux终端连接Linux服务器
    Linux常用命令大全
    linux中的两个命令setfacl和chmod有什么区别
    微信小程序 PHP后端form表单提交实例详解
    Facebook 开源 AI 所使用的硬件平台 'Big Sur'
    十大众筹PC:硅谷新生代如何打造下一代计算机
    <<开源硬件创客 15个酷应用玩转树莓派>>
    别小看树莓派 极客们玩出16个倍儿有趣的项目
    玩转12款Linux开源机器人
    2015业界良心迷你开发板大盘点
  • 原文地址:https://www.cnblogs.com/ivanovcraft/p/9593760.html
Copyright © 2011-2022 走看看