藍橋杯歷屆試題 公式求值

歷屆試題 公式求值  

時間限制:1.0s   內存限制:256.0MB

問題描述

  輸入n, m, k,輸出下面公式的值。
      
  其中C_n^m是組合數,表示在n個人的集合中選出m個人組成一個集合的方案數。組合數的計算公式如下。
http://lx.lanqiao.org/RequireFile.do?fid=TEm3EGfy

輸入格式

  輸入的第一行包含一個整數n;第二行包含一個整數m,第三行包含一個整數k。

輸出格式

  計算上面公式的值,由於答案非常大,請輸出這個值除以999101的餘數。

樣例輸入

3
1
3

樣例輸出

162

樣例輸入

20
10
10

樣例輸出

359316

數據規模和約定

  對於10%的數據,n≤10,k≤3;
  對於20%的數據,n≤20,k≤3;
  對於30%的數據,n≤1000,k≤5;
  對於40%的數據,n≤10^7,k≤10;
  對於60%的數據,n≤10^15,k ≤100;
  對於70%的數據,n≤10^100,k≤200;
  對於80%的數據,n≤10^500,k ≤500;
  對於100%的數據,n在十進制下不超過1000位,即1≤n<10^1000,1≤k≤1000,同時0≤m≤n,k≤n。

提示

  999101是一個質數;
  當n位數比較多時,絕大多數情況下答案都是0,但評測的時候會選取一些答案不是0的數據;

參考答案:
import java.util.Scanner;
public class Gongshi{
    public static void main(String[] args) {
        Scanner C=new Scanner(System.in);
        long n=C.nextInt();
        long m=C.nextInt();
        long k=C.nextInt();
        long p=0;
        long h=zh(m,n);
        //System.out.println(h);
        for(int i=0;i<=n;i++){
            p+=zh(i,n)*ik(i,k);
        }
        System.out.println((p*h)%999101);
        
    }
    public static long jc(long j){            //求j的階乘         
        long sum=1;
        for(int i=1;i<=j;i++){
            sum*=i;
        }
        return sum;
    }
    public static long zh(long a,long b){    //求組合數c(b,a)     
        long t=jc(b)/(jc(a)*jc(b-a));
        return t;
    }
    public static long ik(long x,long y){    //求x的y次方           
        long q=1;
        for(int p=0;p<y;p++){
            q*=x;
        }
        return q;
    }
}

我的代碼 1:初稿

import java.util.Scanner;

public class Result {

	public static void main(String[] args) {
		Scanner sca=new Scanner(System.in);
		int n,m,k,sum=0,h;
		n=sca.nextInt();
		m=sca.nextInt();
		k=sca.nextInt();	
		for(int i=1;i<=n;i++){
		 	sum+=(jc(n)/(jc(i)*jc(n-i)))*(int)Math.pow(i, k);
		}
		h=jc(n)/(jc(m)*jc(n-m));
		System.out.println((sum*h)%999101);
    }

	public static int jc(int a){
		int s=1;
		for(int j=a;j>0;j--){
			s*=j;
		}
		return s;
	}
}

運行結果:

20
10
10
681684

我的代碼 2:改進

import java.util.Scanner;

public class Result {

	public static void main(String[] args) {
		Scanner sca=new Scanner(System.in);
		long n,m,k,sum=0,h;
		n=sca.nextInt();
		m=sca.nextInt();
		k=sca.nextInt();	
		for(int i=1;i<=n;i++){
		 	sum+=(jc(n)/(jc(i)*jc(n-i)))*(long)Math.pow(i, k);
		}
		h=jc(n)/(jc(m)*jc(n-m));
		System.out.println((sum*h)%999101);
    }

	public static long jc(long a){
		long s=1;
		for(long j=a;j>0;j--){
			s*=j;
		}
		return s;
	}
}

因爲當數字很大時int已經不夠放下數字,所以要設爲長整形long。

運行結果:

20
10
10
54327

雖然結果仍然是不對但是有所改進。

正確答案:

用到的知識  lucas定理  歐拉定理 或費馬小定理  快速冪   數學組合公式推導

lucas定理  求C(n,m)%p    

數論Lucas定理:是用來求 c(n,m) mod p的值,p是素數(從n取m組合,模上p)

描述爲:

Lucas(n,m,p)=cm(n%p,m%p)* Lucas(n/p,m/p,p)

Lucas(x,0,p)=1;

cm(a,b)=a! * (b!*(a-b)!)^(p-2) mod p

也= (a!/(a-b)!) * (b!)^(p-2)) mod p

這裏,其實就是直接求 (a!/(a-b)!) / (b!) mod p

由於 (a/b) mod p = a * b^(p-2) mod p


費馬小定理   a^(p-1) mod p=1 (mod p)

import java.math.BigInteger;
import java.util.Scanner;
 
public class L_20END {
    /***
     * @author 林梵
     */
    public static BigInteger lucas(BigInteger n,BigInteger m,BigInteger p){
        if(m.equals(BigInteger.ZERO)) return BigInteger.ONE;
        return BigInteger.valueOf(f(n.mod(p).longValue(),m.mod(p).longValue())).multiply(lucas(n.divide(p),m.divide(p),p)).mod(p);
    }
 
    public static long f(long n,long m){
        if(m>n) return 1;
        if(n==m|| m==0) return 1;
        if(m>n-m) m=n-m;
        long tmpi=1,tmpn=1,s1=1,s2=1,ans=1;
        for (int i = 1; i<=m; i++) {
            tmpi=i;
            tmpn=n-i+1;
            s1=s1*tmpi%999101;
            s2=s2*tmpn%999101;
        }
        ans = s2*pow1(s1,999099)%999101;
        return ans%999101;
    }
    public static long pow1(long x,long n) {
        if(x==1) return 1;
        if (n==0)
            return 1;
        else {
            while ((n & 1)==0) {
                n>>=1;
                x=(x *x)%999101;
            }
        }
        long result = x%999101;
        n>>=1;
        while (n!=0) {
            x=(x *x)%999101;;
            if ((n & 1)!=0)
                result =result*x%999101;
            n>>=1;
        }
        return  result;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        BigInteger n = new BigInteger(sc.nextLine());
        BigInteger m = new BigInteger(sc.nextLine());
        int k = Integer.parseInt(sc.nextLine());
        long start = System.currentTimeMillis();
        BigInteger md = new BigInteger("999101");
        long Cnm=lucas(n, m,md).longValue()%999101;
        long sum = 0;
        if(Cnm!=0){
            int[][] a = new int[k][k];
            int h = 1;
            for (int i = 0; i < k; i++) {
                for (int j = 0; j < k; j++) {
                    if (j >= h)
                        a[i][j] =0;
                    else {
                        if (j == 0 || j == h - 1)
                            a[i][j] = 1;
                        else {
                            a[i][j] = (a[i - 1][j - 1]*(h - j)+a[i - 1][j])%999101;
                        }
                    }
                }
                h++;
            }
            long m1 = 1,n1 =1;
            long x=n.subtract(new BigInteger(k+"")).mod(md.subtract(BigInteger.ONE)).longValue();
            long n3 = pow1(2,x);
            for (int i = k - 1; i >= 0; i--) {
                n1=n3*pow1(2,i)%999101;
                m1 = m1*(n.subtract(new BigInteger((k - 1 - i) + "")).mod(md).longValue())%999101;
                sum = (sum+m1*a[k - 1][i]*n1)%999101;
            }
            sum = sum*Cnm%999101;
        }
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
 
}


--------此代碼------------- 
作者:林梵 
來源:CSDN 
原文:https://blog.csdn.net/u010836847/article/details/21166725?utm_source=copy 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章