问题描述
小蓝正在两台电脑之间拷贝数据,数据是一个 n×mn×m 大小的正整数矩阵,因此总共有 n×m+2n×m+2 个由空格分开的整数,其中前两个整数分别为 nn 和 mm。
然而,有黑客入侵了小蓝的电脑,导致这 n×m+2n×m+2 个正整数的顺序被打乱。小蓝想知道最多可能有多少个不同的原矩阵。
两个矩阵相同当且仅当它们的行数、列数分别相同,且每个位置上的数相同。
输入格式
输入的第一行包含一个正整数 n×m+2n×m+2。
第二行包含 n×m+2n×m+2 个正整数 a1,a2,...,an×m+2a1,a2,...,an×m+2, 相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数,表示可能的不同原矩阵数量。答案可能很大,请输出答案除以 10000000071000000007 的余数。
样例输入
6
2 2 1 4 3 3
样例输出
24
样例说明
- (n,m)=(1,4)(n,m)=(1,4):有 66 种原矩阵:(2,2,3,3)(2,2,3,3), (2,3,2,3)(2,3,2,3), (2,3,3,2)(2,3,3,2), (3,2,2,3)(3,2,2,3), (3,2,3,2)(3,2,3,2), (3,3,2,2)(3,3,2,2);
- (n,m)=(4,1)(n,m)=(4,1):有 66 种原矩阵;
- (n,m)=(2,2)(n,m)=(2,2):有 1212 种原矩阵。
评测用例规模与约定
对于 40%的评测用例,1≤n×m+2≤101≤n×m+2≤10;
对于所有评测用例,1≤n×m+2≤5×1051≤n×m+2≤5×105, 1≤ai≤5×1051≤ai≤5×105。
解题思路
这道题可以理解为:从n*m+2个数中取出来2个数作为矩阵的行数和列数并让kn-1,km-1,然后看剩下的数能组成多少种矩阵,我们可以把矩阵看作一行,对结果并没有影响,那么就转换为求剩下的数有多少种排列组合,公式为
N!
w=------------------------------ %MOD 其中ki是i出现的次数,N为n*m 之后把每种n,m的组合累加即可
k₁!*k₂!。。。kⁿ!
这里需要注意,这个公式我们需要用到乘法逆元,原因是:
N! N!%MOD
------------------------------ %MOD != ------------------------------
k₁!*k₂!。。。kⁿ! ( k₁!*k₂!。。。kⁿ!)%MOD
若 p 是质数,且 a 与 p 互质,则:a^(p-2) ≡ a^(-1) (mod p)即 模 p 下 a 的乘法逆元 = a^(p-2) mod p。
如果仔细观察会发现,每找到一组n,m我们都得重新计算w,所有我们可以先求出所有数的 w,即不减去n和m这两个数,之后w再*n*m,并且(i,j)和(j,i)这两种情况实际上是相同的,让其中一个乘二即可,当n*m=N并且n=m时不需要乘二特判一下就行,阶乘也要预处理一下。
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
int MOD = 1000000007;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int s = Integer.parseInt(br.readLine());
String num[] = br.readLine().split(" ");
int N = s - 2;//矩阵中的个数
long count = 0;//不同原矩阵的总数
int geshu[] = new int[500001];
long sum[] = new long[500001];//阶乘
sum[0] = 1;
long w = 1;//公共系数
for (int i = 1; i <= 500000; i++) {
sum[i] = (sum[i - 1] * i) % MOD;
}
for (int i = 0; i < num.length; i++) {
geshu[Integer.parseInt(num[i])]++;
}
long D = 1;//D为各个数字出现次数的阶乘的积
for (int i = 1; i <= 500000; i++) {
D = (int) (D * sum[geshu[i]] % MOD);
}
long res = 1;//逆元
long pow = MOD - 2;
long base = D;
base = base % MOD; // 先取模避免溢出
while (pow > 0) {
if (pow % 2 == 1) { // 奇数幂,乘到结果中
res = (res * base) % MOD;
}
base = (base * base) % MOD; // 底数平方
pow /= 2; // 指数折半
}
w = (int) (sum[N] * res % MOD);
//枚举n,m并计算count
for (int n=1;n<Math.sqrt(N);n++){
if (N%n==0&&geshu[n]>0){
int m=N/n;
if (geshu[m]>0){
count = (count + 2*w*geshu[n]*geshu[m]%MOD) % MOD;
}
}
}
//n=m且n*m=N的特判
int sqrtN=(int)Math.sqrt(N);
if (sqrtN*sqrtN==N){
if (geshu[sqrtN]>1){
count=(count+w*geshu[sqrtN]*(geshu[sqrtN]-1))%MOD;
}
}
System.out.println(count);
}
}