公式求值 解題報告
歷屆試題 公式求值
時間限制: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;
對於
對於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分程式碼吧:
思路:Lucas定理加上一些數學處理轉換。直接給大家個連結吧http://tieba.baidu.com/p/2832505865。重點可以看看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); } }
十四樓對這道題的處理方法,還是不太懂的話可以草稿紙上演算演算。注意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(這就是那個測試點的問題所在)