雖然是自己寫題解,但是我覺得這個人的題解寫的很好玩2333
先丟一份鏈接23333
http://www.cnblogs.com/OIerShawnZhou/p/7518444.html
描述:
組合數Cmn表示從n個物品中選m個物品的方案數,根據組合數的定義,我們可以知道計算組合數的一般公式:
Cmn=n!/(m!*(n-m)!)
現在,小蔥想知道如果給的n,m,k, 對於0<=i<=n, 0<=j<=min(i,m),有多少對(i,j)滿足Cji是k的倍數
輸入描述 Input Description
第一行有兩個整數t, k,其中t代表該測試點總共有多少組測試數據,k的意義見【問題描述】。
接下來t行每行兩個整數n, m,其中n, m的意義見【問題描述】。
輸出描述 Output Description
t行,每行一個整數代表所有的0<=i<=n,0<=j<=min(i,m)中有多少對(i, j)滿足C(j,i)是k的倍數
樣例輸入 Sample Input
輸入1:
1 2
3 3
輸入2:
2 5
4 5
6 7
樣例輸出 Sample Output
輸出1:
1
輸出2:
0
7
思路:
組合數問題。。。╮(╯_╰)╭ 目前數學上還沒學到,,,就是生物學了學組合數纔會一點
在這裏如果我們打表處理一下的話不難發現這些數的規律是楊輝三角,因此從楊輝三角入手解決這個問題(好像組合數的一個經典解法就是聯繫楊輝三角來着)
首先說這個 c(i,j) = c(i-1,j) + c(i-1,j-1) 數學選修2-3第二章左右介紹的組合數公式。。。。
n只有2000,和清北的一個題目相比起來已經非常小了(抽空我再把清北那個發上來),所以可以考慮先把這個範圍內的所有f[i][j]都先求出來,就用那個公式,全都推出來
邊界是f[i][0] = f[i][i] = 1 ←是組合數的性質2333
爲了求解方便,在推的時候可以把值對k取模,這樣推出來後的f[i][j]如果是0,那麼這個就是k的倍數
令s[i][j]表示在所有的c(i,j) (1≤j≤i)的裏面,爲k的倍數的有多少個,那麼處理數組的時候就是s[i][j] = s[i][j-1] ,每找到一個f[i][j]爲0就讓ans++即可求解
代碼如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define RI register int
typedef long long LL;
const int sz =2500;
LL t,n,m,k,ans;
LL f[sz][sz],s[sz][sz];
inline void read(LL &x){
x=0;bool fl=0;
char ch=getchar();
while(ch<'0'||ch>'9')
{if(ch=='-') fl=1;ch=getchar();}
while(ch>='0'&&ch<='9')
{x=x*10+ch-'0';ch=getchar();}
if(fl)
x*=-1;
}
inline void write(LL x){
if(x<0)
putchar('-'),x*=-1;
if(x/10)
write(x/10);
putchar(x%10+'0');
}
int main()
{
read(t),read(k);
for(RI i=0;i<=2333;++i)
{
f[i][i]=1;
f[i][0]=1;
}
for(RI i=1;i<=2333;++i)
for(RI j=1;j<i;++j)
f[i][j]=(f[i-1][j-1]%k+f[i-1][j]%k)%k;
for(RI i=1;i<=2333;++i)
for(int j=1;j<=i;++j)
{
s[i][j]=s[i][j-1];
if(f[i][j]==0)
s[i][j]++;
}
while(t--)
{
read(n),read(m);
ans=0;
for(LL i=1;i<=n;++i)
{
LL j=min(i,m);
ans+=s[i][j];
}
write(ans),puts("");
}
return 0;
}