蓝桥杯国赛冲刺:数论(二)

第一题:组合数问题

原题链接

题目解析:

我们知道,组合数和杨辉三角其实是相同的

即:从n个物品中分别取0-n个物品的方案数和杨辉三角的第n行的每一个数都是一样的

例如:

从3个数取0,1,2,3个数,分别有1,3,3,1种取法

而在杨辉三角的第3行(不计算最上面的一个1),分别是1,3,3,1

所以这个题我们可以通过杨辉三角的方式进行解答

因为n,m取值较小,2000*2000=4000000,完全可以将所有值计算出来

然后直接判断有多少位置在%k以后为0即可

这里要运用要下二维前缀和计算在它之前有多少个0出现

 当你从n个中,取m个数,那么它应该是所有从m-n个数中取0-m个数为0的方案总和

去m+1个数以上的则与其无关

所以公式中  arr[i][j]=arr[i-1][1]+arr[i][j-1]

这个是说明取和他相同个数的方案数和取比它少个数的方案数的总和

但是在这个运算中,arr[i-1][j-1]会被运算两次,所以减去即可

因此公式即为:arr[i][j]=arr[i-1][j]+arr[i][j-1]-arr[i-1][j-1]

完整代码:

import java.util.Scanner;
 
public class 组合数问题 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int t=sc.nextInt();//t组数据
        int x=sc.nextInt();//对x取模为0的个数
        int [][]arr=new int[2001][2001];//存放每一个数模后对应值
        int [][]brr=new int[2001][2001];//存放到当前位置有多少个k倍
        arr[0][0]=1;
        for (int i=1;i<2001;i++){//简单的杨辉三角
            for (int j=0;j<=i;j++){
                if (j==0||j==i){//以防k为1
                    arr[i][j]=1%x;
                }else{
                    arr[i][j]=(arr[i-1][j]+arr[i-1][j-1])%x;//每一次计算直接取模
                }
            }
        }
        for (int i=1;i<2001;i++){//开始计算出现的次数
            for (int j=1;j<2001;j++){
                //到当前位置时,假设是第n行第m个数,那么就是它前一个(n,m-1)和上一层(n-1,m)获得
                //但是在获得这两个数的时候,(n-1,m-1)这个数运用了两次,所以额外减去
                brr[i][j]=brr[i-1][j]+brr[i][j-1]-brr[i-1][j-1];
                if (arr[i][j]==0&&j<=i) brr[i][j]++;//判断当前位置的杨辉三角是否为0
            }
        }
        for (int i=0;i<t;i++){//输入每组数据
            int n=sc.nextInt();
            int m=Math.min(sc.nextInt(),n);
            System.out.println(brr[n][m]);
        }
    }
}

第二题:排队

原题链接

题目解析:

首先,女生和老师都是有特定的要求,所以我们可以先将男生分配好

但是如果之后再考虑老师会比较麻烦,所以暂时将老师看成两个男生

之后再减去两个老师放在一起的种类即可

那么实际就是n+2名男同学和m名女同学的排列组合

n+2名男同学排列组合有A\large _{n+2}^{n+2}种方案,m名女同学有A\large _{m}^{m}种方案

而n+2名男同学会产生n+3个空位,则有C\large _{n+3}^{m}

所以放入就有A\large _{n+2}^{n+2}*A\large _{m}^{m}*C\large _{n+3}^{m}种方案

然后我们减去两个老师在一起的方案(即将两个老师看成同一个人)

有A\large _{n+1}^{n+1}* A\large _{m}^{m} *C\large _{n+2}^{m}

所以总方案数为: A\large _{n+2}^{n+2}*A\large _{m}^{m}*C\large _{n+3}^{m} - A\large _{n+1}^{n+1}* A\large _{m}^{m} *C\large _{n+2}^{m}

 完整代码:

import java.math.BigInteger;
import java.util.Scanner;

public class 排队 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        if (n+3<m||n+m==0){
            System.out.println(0);
            return;
        }
        BigInteger s=BigInteger.valueOf(1);
        BigInteger t=BigInteger.valueOf(2);
        //(n+2)!
        for (int i=1;i<=n+2;i++) s=s.multiply(BigInteger.valueOf(i));
        //(n+3)!/m!
        for (int i=n+3;i>n+3-m;i--) s=s.multiply(BigInteger.valueOf(i));
        //(n+1)!
        for (int i=1;i<=n+1;i++) t=t.multiply(BigInteger.valueOf(i));
        //(n+2)!/m!
        for (int i=n+2;i>n+2-m;i--) t=t.multiply(BigInteger.valueOf(i));
        s=s.subtract(t);
        System.out.println(s);
    }
}

 第三题:麦森数


原题链接 

题目解析:

快速幂,但是单是2的三百万次就已经超时了,所以没A,过了70%

代码如下:

import java.math.BigInteger;
import java.util.Scanner;

public class 麦森数 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int p=sc.nextInt();
        BigInteger a=BigInteger.valueOf(2);
        a=ksm(a,p).subtract(BigInteger.valueOf(1));
        String s=String.valueOf(a);
        System.out.println(s.length());
        int t=0;
        if (s.length()>=500) {
            s = s.substring(s.length() - 500);
            for (int i=0;i<10;i++){
                for (int j=0;j<50;j++){
                    System.out.print(s.charAt(i*50+j));
                }
                System.out.println();
            }
        }else{
            for (int i=0;i<10;i++){
                for (int j=0;j<50;j++){
                    if (i*50+j+s.length()<500){
                        System.out.print(0);
                    }else{
                        System.out.print(s.charAt(t));
                        t++;
                    }
                }
                System.out.println();
            }
        }
    }
    static BigInteger ksm(BigInteger a, int b){
        BigInteger res = BigInteger.valueOf(1);
        while (b>0){
            if ((b&1)==1){
                res=res.multiply(a);
            }
            a=a.multiply(a);
            b=b>>1;
        }
        return res;
    }
}

第四题:容易题

原题链接

题目初步解析:

一开始做题以为是一个数位dp,但是后来看了一眼n,m的取值范围,我就知道又是一个规律题

我们先看每一位可以选的数值

假设为3,3,然后没有特殊条件

那么第一个数位1,2,3 第二个数位1,2,3 第三个数位1,2,3

那么当所有值为最小,只改变一位数,则1 1 1    1  1  2   1 1 3总和为6

那么当第二个值变为2的时候  第一个值不变,只变第三个值  总和为12

第二个值为3时,总和为18,那么在第二位更改完  总和为6+12+18=36

那么第一位进行变化为2,总和72,变成3位108

总和为36+72+108=216=6*6*6

那么通过一些开放性思维(可以多练练思维题)

可以想到这是一个快速幂

那么如果有特殊条件怎么办?

总共m个格子,我们先找出有多少个格子存在特殊情况(假设x个),剩下的m-x个格子就可以直接快速幂获取

然后剩下的x个格子,分别计算出该格子的值总和,然后依次相乘即可

通过样例进一步解析:

例如样例:

3 4 5

1 1

1 1

2 2

2 3

4 3

那么对应的每一位的值为:5  1  6  3,2,那么没有限制条件的是6,所以快速幂6的1次=6,然后去乘所有的有限制的数,即6*5*1*3=90

那么在计算的时候是可以优先将1-n的累加计算出来,同时去看有多少个值是存在限制的

例如例子中的限制个数为3,分别是:1,2,4这三个位置有限制

那么剩下的个数就是4-3=1,就快速幂完后去乘这三个位置对应的数即可

完整代码:

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Scanner;

public class 容易题 {
    static long mod=1000000007;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        int k=sc.nextInt();
        int [][]arr=new int[k][2];
        for (int i=0;i<k;i++){
            arr[i][0]=sc.nextInt();
            arr[i][1]=sc.nextInt();
        }
        Arrays.sort(arr,((a,b)->{
            if (a[0]!=b[0])
                return a[0]-b[0];
            return a[1]-b[1];
        }));
        long[]brr=new long[k];
        int t=0;//同步计算有多少个不同的数
        BigInteger a= BigInteger.valueOf((1+n));
        BigInteger b= BigInteger.valueOf(n);
        BigInteger y=(a.multiply(b)).divide(BigInteger.valueOf(2)).mod(BigInteger.valueOf(mod));
        if (k==0){
            System.out.println(ksm(y,m,mod));
            return;
        }
        brr[0]=arr[0][1];
        for (int i=1;i<k;i++){
            if (arr[i][0]!=arr[i-1][0]){
                t+=1;
                brr[t]+=arr[i][1];
            }else if (arr[i][1]!=arr[i-1][1]) {
                brr[t] += arr[i][1];
            }
        }
        BigInteger x=ksm(y,m-t-1,mod);
        for (int i=0;i<t+1;i++){
            x=x.multiply((y.subtract(BigInteger.valueOf(brr[i]))));
            x=x.mod(BigInteger.valueOf(mod));
        }
        System.out.println(x);
  }
    static BigInteger ksm(BigInteger a,long b,long c){
        BigInteger res = BigInteger.valueOf(1);
        while (b>0){
            if ((b&1)==1){
                res=res.multiply(a).mod(BigInteger.valueOf(c));
            }
            a=a.multiply(a).mod(BigInteger.valueOf(c));
            b=b>>1;
        }
        return res.mod(BigInteger.valueOf(c));
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值