【做题】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 }

 

posted @ 2017-09-06 16:35  莫名其妙的aaa  阅读(248)  评论(0编辑  收藏  举报