题目链接
分析
这是一道多重集合交错排列的经典题目,经典的题意是这样的:
设集合 A 有
n1个a1,n2个a2,...,ni个ai 问使其相邻两个数不相同的排列有多少种.
我们先来解决这个经典题目,然后再来解决codeforces的这个题目.
可以用 dp+容斥做
先来解决一个简化的问题
设
dp[k]
表示将这些数分成
k
块 每块的数的是一样的,并且每块数量至少为1,那么我们可以通过递推来求这个
设
dp[i][k]
表示考虑前
i
个数分成
dp[i][k]=∑j=1kdp[i−1][k−j](ni−1j−1)∗ni!j!
上面那个不就是个背包吗?递推就好了
知道了 dp[k] 之后,就可以用容斥了,
dp[k]∗k! 表示的是有 n−k 个数相邻
所以有
ans=dp[n]n!−dp[n−1](n−1)!+⋯+(−1)(n−i)dp[i]i!
将原题的完全平方数处理掉就是经典问题了
AC code
#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-10
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 300+10;
const int MAX_V= 500+10;
const int MOD = 1e9+7;
LL a[maxn];
LL C[maxn][maxn];
LL A[maxn];
LL dp[maxn][maxn];
std::map<LL, int> ma;
LL power_mod(LL x,LL n){
LL ret =1;
while (n) {
if(n&1)ret = ret * x %MOD;
x = x*x %MOD;
n >>=1;
}
return ret;
}
void init(){
C[0][0] = 1;
for(int i=1 ; i<maxn ; ++i){
C[i][0] =1;
for(int j=1 ; j<maxn ; ++j){
C[i][j] =(C[i-1][j] + C[i-1][j-1]) % MOD;
}
}
A[0]=1;
for (int i=1 ; i<maxn ; ++i) A[i] = A[i-1]*i % MOD;
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
init();
cin>>n;
for(int i=0 ; i<n ; ++i){
LL x;cin>>x;
for(int j=2 ; j*j <= x ; ++j){
while (x % (j*j) ==0) {
x/=(j*j);
}
}
ma[x] ++;
}
ms(dp,0);dp[0][0] =1;
int cnt =1;
for(auto e : ma){
for(int i=1 ; i<=n ; ++i){
//dp[cnt-1][0] =1;
int m = min(e.se,i);
for(int j = 1 ; j<=m ; ++j){
LL tmp = A[e.se];
tmp = tmp * C[e.se-1][j-1] % MOD;
tmp = tmp *power_mod(A[j],MOD-2) % MOD;
dp[cnt][i] += dp[cnt-1][i-j]*tmp;
dp[cnt][i] %= MOD;
}
}
cnt ++;
}
//for(int i=0 ; i<=n ; ++i)std::cout << dp[cnt-1][i] << '\n';
LL ans =dp[cnt-1][n] *A[n]% MOD;int k=0;
for(int i =n-1 ; i>=0 ; --i){
if(k&1)ans = (ans+dp[cnt-1][i]*A[i] )% MOD;
else{
ans = (ans - dp[cnt -1][i] * A[i] + MOD) % MOD;
}
k ^=1;
}
std::cout << ans << '\n';
//std::cout << "time "<< clock()/1000 <<"ms"<< '\n';
return 0;
}