【题解】CF840C On the Bench
题目描述:
给定一个序列 a(ai≤109),长度为 n(n≤300)。
试求有多少 1 到 n 的排列 pi,满足对于任意的 2≤i≤n 有 api−1×api 不为完全平方数,答案对 109+7 取模。
题目分析:
(我竟然场切了,太离谱了)
如果对于这个题面硬做的话,其实就是若 ai×aj 不为完全平方数,则连边 (i,j)。
问题就转化为了给定一张图,求其哈密顿路的条数,这个东西显然不能做。
所以也就是说这张图必然存在某些特殊性质。
考虑若 A×B 为完全平方数那么意味着什么呢,考虑将 A,B 写成它的标准分解式也就是 A=∏piai,B=∏pibi,这样 A×B=∏piai+bi,其为完全平方数也就是对于每一个 ai+bi 其均为偶数,即 ai≡bi(mod2)。
所以我们可以一开始就对所有的数分解为其标准分解式,然后将其指数模 2,这样的问题就转化为了相邻两个位置上的数不相等。
可以考虑一种一种数地插入,然后 dp 去统计这个过程的方案数。
也就是设 dpi,j 表示插入了前 i 种数,有 j 个位置存在相邻两个位置的数相等。
转移就是考虑新加入的这一种数,会将多少个原本相邻且相等的位置占据,又会产生多少个相邻且相等的位置,带上一些组合系数去转移即可。
可以发现当我们确定了当前新加入的数内部会造成多少个相邻且相等的位置时,我们就相当于将其分成了固定的几段,也就是可以视为几个小球插入到序列里。
因为所有的数的出现次数之和为 n,所以复杂度为 O(n3)。
代码:
点击查看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include<bits/stdc++.h> #define int long long using namespace std; const int N = 305; const int MOD = 1e9+7; int a[N],b[N],tot,dp[N][N],pre[N],inv[N],fac[N]; int power(int a,int b){ int res = 1; while(b){ if(b & 1) res = res * a % MOD; a = a * a % MOD; b >>= 1; } return res; } int binom(int n,int m){ if(n < m || n < 0 || m < 0) return 0; return fac[n] * inv[m] %MOD * inv[n-m] % MOD; } int get(int x){ int tmp = 1; for(int i=2; i*i<=x; i++){ int cnt = 0; while(x % i == 0){ x /= i,++cnt; } if(cnt & 1) tmp = tmp * i; } if(x > 1) tmp = tmp * x; return tmp; } void add(int &a,int b){ a = (a + b) % MOD; } signed main(){ int n;scanf("%lld",&n); for(int i=1; i<=n; i++) scanf("%lld",&a[i]); bool flag = false; for(int i=1; i<=n; i++){ if(a[i] == 0) flag = true; } if(flag){ if(n == 1) printf("1\n"); else printf("0\n"); return 0; } for(int i=1; i<=n; i++) a[i] = get(a[i]); sort(a+1,a+n+1); int sz = 1,pos = 1; for(int i=2; i<=n; i++){ if(a[i] == a[pos]) ++sz; else b[++tot] = sz,sz = 1,pos = i; } b[++tot] = sz; for(int i=1; i<=tot; i++) pre[i] = pre[i-1] + b[i]; fac[0] = 1; for(int i=1; i<=n; i++) fac[i] = (fac[i-1] * i)%MOD; inv[n] = power(fac[n],MOD-2); for(int i=n-1; i>=0; i--) inv[i] = (inv[i+1] * (i+1)) % MOD; dp[0][0] = 1; for(int i=0; i<tot; i++){ for(int j=0; j<=pre[i]+1; j++){ if(!dp[i][j]) continue; for(int k=0; k<=b[i+1]-1; k++){ int p = (b[i+1]-1) - k + 1; int tmp = binom(b[i+1]-1,p-1) * fac[b[i+1]] % MOD; for(int x=0; x<=j; x++){ add(dp[i+1][j+k-x], dp[i][j] * binom(j,x) % MOD * binom(pre[i] + 1 - j,p-x) % MOD * tmp %MOD); } } } } printf("%lld\n",dp[tot][0]); return 0; }
|