• hdu 6143


    题意:有m种字符,要求构造两段长度为n的字符串,其中这两段不能有相同的字符

    枚举左边选了i种字符,右边可以选1,2....min(n,m-i)种字符

    这样就把问题转化为用k种字符构造n长度的字符串的种类有多少种

    容斥:单独考虑每一位上的字符都有k种选择,k^n,但会有不够k种的情况,所以要减去只有k-1种颜色,只有k-2种颜色。。。。的情况

    这里是用容斥处理一下

    好,上面都是copy过来的东西,现在是自己写的了。。 首先,这道题目比赛的时候没写出来,虽然当时想到了用容斥处理,但是对容斥的情况没有理解清楚。

    复习一下容斥,写容斥的时候,我们先要理出最小集合,这些集合的并能够得到我们想到的结果;在在就是集合的交有了一个新的认识,以前的理解太肤浅了。

    对于这道题目,我们定理f(x)为x个字母全部用上的方案数,利用容斥,一般要考虑逆问题。f(x)的逆问题———k个元素中,至少有一个字母没用上的方案数。

    那么最小集合为有一个字母没用上,其他字母可用可不用的情况,之后的容斥就可以了。

    附上一个骚气的代码(自己写的老是超时,有点烦,贴了个其他大佬的):

    #include <cstdio>
    #include <iostream>
    typedef long long LL;
    using namespace std;
    const int maxn =2005;
    const int mod = 1e9+7;
    
    LL res[maxn];
    LL C[maxn][maxn];
    LL mul[maxn][maxn];
    
    LL mult(LL a,LL b)
    {
        if(mul[a][b])return mul[a][b]; // 这个记录的方式也是。。。 长见识了
        LL ans=1,aa=a,bb=b;
        a%=mod;
        while(b)
        {
            if(b&1)ans=(ans*a)%mod;
            a=(a*a)%mod;
            b>>=1;
        }
        mul[aa][bb]=ans;
        return ans;
    }
    
    void init()
    {
        C[0][0]=1;
        for(int i=1; i<maxn; i++)
            for(int j=0; j<=i; j++)
                C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    
    LL getget(LL n,LL m)
    {
        LL cnt=0;
        for(int k=0; k<=m; k++)
        {
            if(k&1)cnt=(cnt-C[m][k]*mult(m-k,n)%mod)%mod;
            else cnt=(cnt+C[m][k]*mult(m-k,n)%mod+mod)%mod;
        }
        return cnt;
    }
    
    template <class T>
    inline void scan_d(T &ret)
    {
        char c;
        ret = 0;
        while ((c = getchar()) < '0' || c > '9');
        while (c >= '0' && c <= '9')
            ret = ret * 10 + (c - '0'), c = getchar();
    }
    
    void Out(LL a)
    {
        if (a < 0)
        {
            putchar('-');
            a = -a;
        }
        if (a >= 10)
            Out(a / 10);
        putchar(a % 10 + '0');
    }
    
    int main()
    {
        init();
        int T;
        scan_d(T);
        while(T--)
        {
            memset(res,0,sizeof(res));
            int n,m;
            LL ans=0;
            scan_d(n);
            scan_d(m);
            for(int i=1; i<m; i++)
                res[i]=getget(n,i);
            for(int i=1; i<m; i++)
                for(int j=i; j<=m-i; j++)// 这里枚举两个数的时候,采用有序枚举的形式,降低一点复杂度,还可以去重。
                {
                    LL aa=C[m][i]*res[i]%mod;
                    LL bb=C[m-i][j]*res[j]%mod;
                    LL cnt = (aa*bb)%mod;
                    if(i!=j) cnt = (cnt+cnt)%mod;
                    ans=(cnt+ans)%mod;
                }
            Out(ans);
            putchar('
    ');
        }
        return 0;
    }
  • 相关阅读:
    540 Single Element in a Sorted Array 有序数组中的单一元素
    539 Minimum Time Difference 最小时间差
    538 Convert BST to Greater Tree 把二叉搜索树转换为累加树
    537 Complex Number Multiplication 复数乘法
    535 Encode and Decode TinyURL 编码和解码精简URL地址
    532 K-diff Pairs in an Array 数组中差为K的数对
    530 Minimum Absolute Difference in BST 二叉搜索树的最小绝对差
    529 Minesweeper 扫雷游戏
    526 Beautiful Arrangement
    1.5 函数
  • 原文地址:https://www.cnblogs.com/z1141000271/p/7391314.html
Copyright © 2020-2023  润新知