[CF840C] On the Bench

问题描述

给定一个序列 \(a(a_i\le 10^9)\),长度为 \(n(n\le 300)\)

试求有多少 1 到 \(n\) 的排列 \(p_i\),满足对于任意的 \(2\le i\le n\)\(a_{p_{i-1}}\times a_{p_i}\) 不为完全平方数,答案对 \(10^9+7\) 取模。

样例输入

3
1 2 4

样例输出

2

解析

一个数可以拆成一个完全平方数乘上另一个数。不妨将每个数的完全平方数因子去掉,问题就变成了如何使一个序列的相邻两个数不同。

考虑从小到大依次放数。设 \(f_{i,j,k}\) 表示当前考虑第 \(i\) 个数,一共有 \(j\) 个数相邻,其中 \(k\) 个与当前数相同。记 \(cnt\) 表示前面有几个数和自己相同。有如下几种情况:

  • 当前数放在某个和自己相同的数旁边。容斥一下,有\(f_{i,j+1,k+1}=f_{i-1,j,k}\times (2*cnt-k)\)
  • 放在某两个相邻的数中间,但不跟自己相同。\(f_{i,j-1,k}=f_{i-1,j,k}\times (j-k)\)
  • 放在了其他地方。\(f_{i,j,k}=f_{i-1,j,k}\times [i-(2cnt-k)-(j-k)]\)

当前数与前面放的数不同时,注意对DP数组进行修改。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
#define N 302
using namespace std;
const int mod=1000000007;
int n,i,j,k,a[N],f[2][N][N],x,cnt;
int read()
{
	char c=getchar();
	int w=0;
	while(c<'0'||c>'9') c=getchar();
	while(c<='9'&&c>='0'){
		w=w*10+c-'0';
		c=getchar();
	}
	return w;
}
signed main()
{
	n=read();
	for(i=1;i<=n;i++) a[i]=read();
	for(i=1;i<=n;i++){
		for(j=2;j*j<=a[i];j++){
			while(a[i]%(j*j)==0) a[i]/=j*j;
		}
	}
	sort(a+1,a+n+1);
	f[0][0][0]=1;
	for(i=2;i<=n;i++){
		x^=1;
		memset(f[x],0,sizeof(f[x]));
		if(a[i]==a[i-1]) cnt++;
		else{
			for(j=0;j<i-1;j++){
				for(k=1;k<=min(cnt,j);k++){
					f[x^1][j][0]=(f[x^1][j][0]+f[x^1][j][k])%mod;
					f[x^1][j][k]=0;
				}
			}
			cnt=0;
		}
		for(j=0;j<i-1;j++){
			for(k=0;k<=min(cnt,j);k++){
				if(j>0) f[x][j-1][k]=(f[x][j-1][k]+f[x^1][j][k]*(j-k)%mod)%mod;
				f[x][j][k]=(f[x][j][k]+f[x^1][j][k]*(i-2*cnt-j+2*k)%mod)%mod;
				f[x][j+1][k+1]=(f[x][j+1][k+1]+f[x^1][j][k]*(2*cnt-k)%mod)%mod;
			}	
		}
	}
	printf("%lld\n",f[x][0][0]);
	return 0;
}

posted @ 2020-08-24 16:06  CJlzf  阅读(97)  评论(0编辑  收藏  举报