【 NOIP 2016 】【 组合数问题 】

luogu传送门

刷水有益健康->实际上除了增长信心外,,没什么作用了,, 这道题 感谢 LZH dalao,嗯,总之是生命中比较重要的一个人吧。。。扯远了

看到数据的那一刻,不想到 n^{2} 算法都不容易 -> 递推 杨辉三角 (LZH dalao 告诉本蒟蒻的 : 一脉相承,很简单的)

不过因为多组数据,所以 GG 了,我们可以明显的发现 k 是固定的,所以,整个 DP 好了 , DP[ i ] [j] 表示 C_{i}^{j}  中 满足 取模条件的 个数 ,然后 就可以O(1)查询了

然鹅,这样只有 70 分,因为即使是 long long 也会溢出,于是 要在 递推时 就取模,并将 DP 中的条件 加成 ==0 (原来是 %取模后等于零)

虽然说,里面涉及 容斥 ,然鹅,容斥 很简单,很好想。。。

递推 杨辉三角 :-> 当然是预处理了:)

			Cnm[i][j]=( Cnm[i-1][j-1]+Cnm[i-1][j] ) % (LL)k;

dp : 容斥(一个 好听 的名字)

			dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];

细节:

		dp[i][i+1]=dp[i][i];

【 附上 AC 代码 】

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL long long 
using namespace std;

inline int wread(){
	char c(getchar ());int wans(0),flag(1);
	while (c<'0' || c>'9'){if (c=='-') flag=-1; c=getchar();}
	while (c>='0' && c<='9'){wans=wans*10+c-'0';c=getchar();}
	return wans*=flag;
}

LL Cnm[2003][2003];
LL dp[2003][2003];

int main (){
	int t(wread()), k(wread());
	for (int i(0);i<=2000;++i){
		if (i==0)	{Cnm[0][0]=1;continue;}
		for (int j(0);j<=i;++j){
			if (j==0)	{Cnm[i][0]=1; continue;}
			Cnm[i][j]=( Cnm[i-1][j-1]+Cnm[i-1][j] ) % (LL)k;
		}
	}
	
	for (int i(1);i<=2000;++i){
		for (int j(1);j<=i;++j){
			dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];
			if (Cnm[i][j]==0)	dp[i][j]++;
		}
		dp[i][i+1]=dp[i][i];
	}
	
	while (t--){
		int i(wread()),j(wread());
		printf("%lld\n",dp[i][min(j,i)]);
	}
	return 0;
}
扫描二维码关注公众号,回复: 2571835 查看本文章

猜你喜欢

转载自blog.csdn.net/violinlove/article/details/81393420