传送门
思路:
利用公式: C( n,r ) = C( n-1,r ) + C( n-1,r-1 )
由此可以将计算 C( n,r ) 的过程化为加法来做。
可以看出,C( n,r ) 其实就是求杨辉三角的第 n 行、第 r 列上的数(行列从 0 开始)。
先 N2 暴力地预处理出杨辉三角的各个项,用前缀和记录每一项之前能被 k 整除的排列对数。
对于每次询问,只要 O(1) 的时间,就能输出答案。
Code:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<cstdlib> #include<stack> #include<vector> #include<queue> #include<deque> #include<map> #include<set> using namespace std; #define lck_max(a,b) ((a)>(b)?(a):(b)) #define lck_min(a,b) ((a)<(b)?(a):(b)) typedef long long LL; const int maxn=2003; LL n,m,k,T,c[maxn][maxn],f[maxn][maxn]; inline LL read() { LL kr=1,xs=0; char ls; ls=getchar(); while(!isdigit(ls)) { if(!(ls^45)) kr=-1; ls=getchar(); } while(isdigit(ls)) { xs=(xs<<1)+(xs<<3)+(ls^48); ls=getchar(); } return xs*kr; } inline void out(LL xs) { if(!xs) {putchar(48); return;} if(xs<0) putchar('-'),xs=-xs; LL kr[57],ls=0; while(xs) kr[++ls]=xs%10,xs/=10; while(ls) putchar(kr[ls]+48),ls--; } inline void work() { c[1][1]=1; for(LL i=0;i<maxn;i++) c[i][0]=1; for(LL i=2;i<maxn;i++) for(LL j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%k; for(LL i=2;i<maxn;i++) { for(LL j=1;j<=i;j++) { f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]; if(c[i][j]==0) f[i][j]+=1; } f[i][i+1]=f[i][i]; } } int main() { freopen("problem.in","r",stdin); freopen("problem.out","w",stdout); T=read();k=read(); work(); while(T--) { n=read();m=read(); m=lck_min(n,m); out(f[n][m]),putchar(' '); } fclose(stdin); fclose(stdout); return 0; }