1. 程式人生 > >公式求值 解題報告

公式求值 解題報告

歷屆試題 公式求值  

時間限制:1.0s   記憶體限制:256.0MB

問題描述

  輸入n, m, k,輸出下面公式的值。

       

  其中C_n^m是組合數,表示在n個人的集合中選出m個人組成一個集合的方案數。

輸入格式

  輸入的第一行包含一個整數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的資料;

藍橋杯上歷屆試題實在刷不動了,再加上自己可能搜尋關鍵字有點問題吧,連找出答案都有點困難。

不過今天總算搜出了這道公式求值的解題思路,再借鑑大神們的程式碼,總算把這道“過”了

(其實測試資料裡面應該有一個是有問題的,程式碼改動一行倒是能過該點~~~)

下面我就貼出自己的90分程式碼吧:

import java.math.BigInteger;
import java.util.Scanner;
class Number {
	long x,y,dd;

	/**
	 * @param x
	 * @param y
	 * @param dd
	 */
	public Number(long x, long y, long dd) {
		super();
		this.x = x;
		this.y = y;
		this.dd = dd;
	}

	/**
	 * 
	 */
	public Number() {
		super();
		// TODO Auto-generated constructor stub
	}
	
}
public class Main {
	static BigInteger n, m;
	static int k;
	static long monum = 999101;
	static BigInteger mobig = new BigInteger("999101");
	static BigInteger big2 = new BigInteger("2");
	static long ansnum1,ans;
	static long dp[][], bignum[], subnum[];
	static long fact[];
    private static Number gcd(long a,long b) {
	  if (b==0) return new Number(1,0,a);
	  Number number=gcd(b, a%b);
	  long x=number.y;
	  long y=number.x-(a/b)*number.y;
	  long dd=number.dd;
	  return new Number(x,y,dd);
    }
    private static long mod_inverse(long num) {
    	if (num==0) return 0;
    	Number number=gcd(num, monum);
    	long x=(number.x+monum)%monum;
    	return x;
    }
	private static long cal(BigInteger num) {
		if (num.equals(BigInteger.ZERO)) return 1;
		if (num.equals(BigInteger.ONE)) return 2;
		long mnum = cal(num.divide(big2));
		mnum = mnum*mnum%monum;
		BigInteger mo = num.mod(big2);
		if (mo.equals(BigInteger.ONE))
			mnum=mnum*2%monum;
		return mnum;
	}

	private static void init() {
		fact = new long[(int) (monum + 1)];
		fact[0] = 1;
		for (int i = 1; i <= monum; i++) 
			fact[i]=(fact[i-1]*(long)i)%monum;
	}
	private static long calc(int n,int m) {
		if (n<m) return 0;
		long mo=fact[m]*fact[n-m]%monum;
		long divnum=mod_inverse(mo);
		long res=fact[n]*divnum%monum;
		return res;
	}
    private static long lucas(BigInteger n,BigInteger m){
    	if (m.equals(BigInteger.ZERO)) return 1;
    	int nmo=n.mod(mobig).intValue();
    	int mmo=m.mod(mobig).intValue();
    	return calc(nmo, mmo)*lucas(n.divide(mobig),m.divide(mobig))%monum;
    }
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner reader = new Scanner(System.in);
		n = reader.nextBigInteger();
		m = reader.nextBigInteger();
		k = reader.nextInt();
		BigInteger kbig=new BigInteger(String.valueOf(k));
		dp = new long[k + 1][k + 1];
		dp[0][0]=1;
		long mon=n.mod(mobig).longValue();
		for (int i = 0; i <= k - 1; i++)
			for (int j = 0; j <= i; j++) {
				dp[i + 1][j] = (dp[i+1][j]+(long)j*dp[i][j])%monum;
				dp[i + 1][j + 1] =(dp[i+1][j+1]+(long)(mon-j+monum)*dp[i][j])%monum;
			}
		long mulnum =cal(n.subtract(kbig));
		for (int i = k; i >= 0; i--) {
			ansnum1 =(ansnum1+dp[k][i]*mulnum)%monum;
			mulnum = (mulnum*2)%monum;
		}
		init();
		ans=ansnum1*lucas(n, m)%monum;
		System.out.println(ans);
	}

}
思路:Lucas定理加上一些數學處理轉換。直接給大家個連結吧http://tieba.baidu.com/p/2832505865。重點可以看看

十四樓對這道題的處理方法,還是不太懂的話可以草稿紙上演算演算。注意dp[i][j]代表第i次求導後(x^j)*(1+x)^(n-j)的係數。然後具體寫程式碼的時候,方法返回型別儘量處理為long,能取模就取模,儘量少用大數直接加減乘除運算操作,不然後面的資料會超時(我一開始就直接用大數寫,最後兩個點超時了,前面幾個點也比較靈異地出現了錯誤答案)。其他標程逆元部分似乎是利用費馬小定理,然後a^(p-2)模p為a模p的逆元,而我是直接利用擴充套件歐幾里得求a模p的逆元。對了,還要注意lucas定理利用時,如果c(n%p,m%p)中n%p<m%p,則值應直接返回0而不是1(這就是那個測試點的問題所在)