蓝桥杯 20531黑客java

问题描述

小蓝正在两台电脑之间拷贝数据,数据是一个 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

样例说明

  1. (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);
  2. (n,m)=(4,1)(n,m)=(4,1):有 66 种原矩阵;
  3. (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);
    }
}
相关推荐
LOVE️YOU2 小时前
Python 中 Tuple 为什么有时可 Hash,有时不可 Hash?
开发语言·python·哈希算法
ym hyd 1113 小时前
试题库管理系统源码 Java+SpringBoot+Vue3 前后分离
java·vue.js·spring boot·毕设
李永奉3 小时前
C语言-指针函数与函数指针及回调函数的使用
c语言·开发语言
小oo呆3 小时前
【学习心得】迭代器和可迭代对象
开发语言·python
AC赳赳老秦3 小时前
采集行为合规自检:OpenClaw 自动校验 robots 协议与采集频率,规避违规采集风险
java·开发语言·c++·python·php·deepseek·openclaw
钱栈up3 小时前
mvn compile 卡死在 javac 阶段:一次类型不匹配的排查与修复
java·开发语言
迅猛龙办公室3 小时前
Python输出当前计算机的系统日期和时间
开发语言·前端·python
杨丰玮4183 小时前
C语言基础知识(一)————各类符号的含义与输入输出库(stdio)
c语言·开发语言
剑胆凌锋4 小时前
等级保护测评工程师的困境
网络·安全·web安全·网络安全·职场和发展
程序猿编码4 小时前
工业视觉多任务方案:可编程梯度信息视觉框架,训练评估 + C++ 部署完整实战
开发语言·c++·视觉·推理引擎