• CF840C On the Bench 解题报告


    CF840C On the Bench

    题意翻译

    给定(n) ((1≤n≤300)) 个数,求问有多少种排列方案使得任意两个相邻的数之积都不是完全平方数。由于方案数可能很大,输出方案数 (mod) (10^9+7)的值。


    首先每个数的每个质因子的幂可以先(mod 2),然后问题转化成两两不相等的方案数了。

    证明也很简单,这些质因子的2次方是没有用的,洛谷的题解有一种更加美妙的方法进行了解释。

    然后把每一组相等的数划分到一个集合里去。

    (dp_{i,j})表示前(i)个集合有(j)对相等位置的排列数。

    给出填表法的转移方程再进行具体解释

    (dp_{i,j+l-k}+=dp_{i-1,j} imes (fac_{ct} imes inom{ct-1}{ct-1-l}) imes (inom{j}{k} imes inom{st+1-j}{ct-l-k}))

    两个括号的内容是独立考虑的,有些变量的意义慢慢说。


    首先考虑第一个括号的内容

    (l)表示第(i)个集合自身产生了(l)个相等的数,(ct)是第(i)个集合元素的个数,(fac)是阶乘

    (inom{ct-1}{ct-1-l})表示把(st)有序元素分成(st-l)有序集合的方案数

    可以从类似插板法的方法来说明,就是往空挡里面插东西。

    因为这样的集合划分是有序的,而我们每个元素是可以无序的,所以乘上排列数,也就是阶乘。

    所以第一个括号就是求出了把(i)这个集合分成了(ct-l)个集合的方案数,同时,Ta们是有序的。


    第二个括号是把这(ct-l)个集合往空挡里面插的方案数。

    (k)表示有(k)对相等的位置被插开了,(st)表示前(i-1)个集合的总元素个数

    于是我们把这些东西分开的去插开别人或者不插开别人的方案数乘起来就可以了。


    要枚举(k)(l),复杂度说不清楚( ext{QAQ})


    Code:

    #include <cstdio>
    #include <algorithm>
    #define ll long long
    const int N=300;
    const ll mod=1e9+7;
    int a[N+10],b[N+10],cnt[N+10],tt[N+11],n,m;
    ll C[N+10][N+10],dp[N+10][N+10],fac[N+10];
    void init()
    {
        C[0][0]=1;
        for(int i=1;i<=N;i++)
        {
            C[i][0]=1;
            for(int j=1;j<=i;j++)
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
        fac[0]=1;
        for(int i=1;i<=N;i++)
            fac[i]=fac[i-1]*i%mod;
    }
    int min(int x,int y){return x<y?x:y;}
    int main()
    {
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            for(int j=2;j*j<=a[i];j++)
                while(a[i]%(j*j)==0) a[i]/=j*j;
        }
        std::sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)
        {
            if(a[i]==b[m]) ++cnt[m],++tt[m];
            else b[++m]=a[i],cnt[m]=1,tt[m]=tt[m-1]+1;
        }
        dp[1][cnt[1]-1]=fac[cnt[1]];
        for(int i=1;i<m;i++)
            for(int j=0;j<tt[i];j++)//相等个数
            {
                if(!dp[i][j]) continue;
                for(int k=0;k<=min(j,cnt[i+1]);k++)//堵几个?
                    for(int l=0;l<=cnt[i+1]-k;l++)//搞几个?
                    {
                        if(j+l-k>=tt[i+1]||j+l-k<0) continue;
                        (dp[i+1][j+l-k]+=
                         dp[i][j]*C[j][k]%mod*
                         C[tt[i]+1-j][cnt[i+1]-l-k]%mod*
                         fac[cnt[i+1]]%mod*
                         C[cnt[i+1]-1][cnt[i+1]-1-l]%mod)%=mod;
                    }
            }
        printf("%lld
    ",dp[m][0]);
        return 0;
    }
    

    2018.10.15

  • 相关阅读:
    3配置
    1开机初始化配置
    shell <<EOF
    Sun SPARC Enterprise M5000 启动步骤
    CISCO MDS – Useful ‘Show’ Commands
    oracle 内存不足处理
    mysql 日志类型
    MySQL 学习
    抓取进程中包括其所有线程的iowait时间
    每天网络半小时(MAC数据包在哪里合并的)
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9794153.html
Copyright © 2020-2023  润新知