zoukankan      html  css  js  c++  java
  • 【做题】Codeforces Round #429 (Div. 2) E. On the Bench——组合问题+dp

    题目大意是给你n个数,求相邻两数相乘不是完全平方数的排列数。
    一开始看到这题的时候,本人便想给相乘为完全平方数的数对建边,然后就写萎了...
    后来通过集体智慧发现这个重要性质:对于自然数a,b,c,若a*b为完全平方数,且b*c为完全平方数,那么a*c就是完全平方数。(我居然没想到)因此就可以对这n个数进行分组,使得每一组中两个数两两相乘为完全平方数,不同组的数两两相乘不是完全平方数。假设分成tot组,第i组的数的个数为num[i]。这也就等同于相同的数不能相邻的排列问题。
    于是通过PY就得到了动规的做法:
    定义数组dp[i,j],其中i表示前i组数,j表示有多少对相邻的同组的数(这也等价于有j个地方需要插入其他的数),而dp[i,j]就表示在当前状态下的方案数。那么最终答案就是dp[tot,0]。
    定义m为前i-1组数的元素个数之和,在dp过程中维护。
    当新加入第i组数的时候,把这num[i]个数分成k份(方案数是C(num[i]-1,k-1)),插入到m+1个空位中。
    此时需要分类讨论:
    在这m+1个空位中,有j个空位是特殊的,若插入其中会影响新生成的排列的相邻同组数的数目。于是设k份中的p份插入这j个空位中,剩下k-p份插入m+1-j个空位中。
    就可以得到dp[i,j+num[i]-k-p]+=dp[i-1,j]*C(num[i-1],k-1)*C(j,p)*C(m+1-j,k-p);
    这我一开始也不是很理解...
    当然代码与这里有所差异。
    注:因为dp是基于每一组数中的元素相同的前提的,所以最后答案还要乘以每一组数的A(num[i],num[i])。
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int N=310,MOD=1000000007;
     4 long long num[N],c[N][N];
     5 long long dp[N][N],a[N];
     6 int tmp,n,tot,example[N];
     7 bool key;
     8 bool ask(int x,int y)
     9 {
    10     long long re=1;
    11     re*=x;
    12     re*=y;
    13     long long f=sqrt(re);
    14     if(f*f==re) return 1;
    15     return 0;
    16 }
    17 int main()
    18 {
    19     cin>>n;
    20     for(int i=1;i<=n;i++)
    21     {
    22         cin>>tmp;
    23         key=1;
    24         for(int j=1;j<=tot;j++)
    25         {
    26             if(ask(tmp,example[j]))
    27             {
    28                 num[j]++;
    29                 key=0;
    30                 break;
    31             }
    32         }
    33         if(key)
    34         {
    35             num[++tot]=1;
    36             example[tot]=tmp;
    37         }
    38     }
    39     for(int i=0;i<=n;i++)
    40      c[i][0]=1;
    41     for(int i=1;i<=n;i++)
    42      for(int j=1;j<=i;j++)
    43      {
    44          c[i][j]=c[i-1][j]+c[i-1][j-1];
    45          c[i][j]%=MOD;
    46      }
    47     int m=0;
    48     a[1]=1;
    49     for(int i=2;i<=n;i++)
    50     {
    51         a[i]=a[i-1]*i;
    52         a[i]%=MOD;
    53     }
    54     dp[0][0]=1;
    55     long long temp,temp1;
    56     for(int i=1;i<=tot;i++)
    57     {
    58         for(int j=0;j<n&&j<=m+1;j++)
    59         {
    60             if(dp[i-1][j]==0) continue;
    61             for(int k=0;k<num[i];k++)
    62             {
    63                 temp=dp[i-1][j]*c[num[i]-1][k];temp%=MOD;
    64                 for(int p=0;p<=k+1&&p<=j+num[i]-1-k;p++)
    65                 {
    66                     temp1=temp*c[j][p];temp1%=MOD;//本人因为数据溢出炸了几次
    67                     dp[i][j+num[i]-1-k-p]+=(temp1*c[m+1-j][k+1-p])%MOD;
    68                     dp[i][j+num[i]-1-k-p]%=MOD;
    69                 }
    70             }
    71         }
    72         m+=num[i];
    73     }
    74     for(int i=1;i<=tot;i++)
    75     {
    76         dp[tot][0]*=a[num[i]];
    77         dp[tot][0]%=MOD;
    78     }
    79     cout<<dp[tot][0]<<endl;
    80     return 0;
    81 }
  • 相关阅读:
    梯度下降-Momentum
    6-10-HIRP OPEN 2016
    机器学习数目推荐
    《统计、计算和未来-李航》读后感
    MongoDB $关键字 $修改器
    MongoDB数据类型
    MongoDB操作(增删改查)
    MongoDB安装及开启(Windows)
    第七周:论文泛读(二)
    第六周:论文泛读(一)
  • 原文地址:https://www.cnblogs.com/cly-none/p/7467395.html
Copyright © 2011-2022 走看看