luogu P2822 组合数问题

题面传送门
对于这道题,其实题目很简单,暴力很好打。
想法 1 1 1:对于每组输入数据,暴力判断两重循环枚举,一重循环计算组合数。时间复杂度 O ( t n 3 ) O(tn^3) Otn3,大概 30 30 30
想法 2 2 2:把每组数的组合存下来,直接调用。时间复杂度 O ( t n 2 ) O(tn^2) Otn2大概 35 35 35分。
想法 3 3 3:既然直接算组合数会爆精度,那么在计算组合数是就不能有除法和减法,只有加法和乘法满足 ( a % c ) ( b % c ) % c = ( a b ) % c (a\%c)(b\%c)\%c=(ab)\%c (a%c)(b%c)%c=(ab)%c ( a % c + b % c ) % c = ( a + b ) % c (a\%c+b\%c)\%c=(a+b)\%c (a%c+b%c)%c=(a+b)%c.然后我们自然想起杨辉三角,这是一个求组合数的东西,递推公式为 f i , j = f i − 1 , j + f + i − 1 , j − 1 f_{i,j}=f_{i-1,j}+f+{i-1,j-1} fi,j=fi1,j+f+i1,j1,所以我们可以递推出来,一遍递推一遍%k,对于每组数据递推一遍,若 % k = = 0 \%k==0 %k==0即有一个答案。大概有 70 70 70分。
想法 4 4 4:因为杨辉三角是一样的,所以可以先递推出来。
递推出来有什么用啊,还不是要 O ( n 2 ) O(n^2) O(n2)查询。
这可是提高组原题,提高组不考数据结构考什么。
那么有一种数据结构,修改很麻烦,但查询 O ( 1 ) O(1) O(1)
所以我们可以用前缀和过掉这一题。
关于二维前缀和之前的题解已经有讲过,不再赘述。
代码实现:

#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,x,y,f[2039][2039],s[2039][2039],ans,tot,pus;
int main(){
	register int i,j;
	scanf("%d%d",&n,&m);
	f[1][0]=f[1][1]=1;
	for(i=2;i<=2000;i++){
		f[i][0]=1;
		for(j=1;j<=i;j++){
			f[i][j]=(f[i-1][j]+f[i-1][j-1])%m;
			if(!f[i][j]) s[i][j]=1;
			s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
		}
		s[i][i+1]=s[i][i];
	}
	for(i=1;i<=n;i++){
		scanf("%d%d",&x,&y);
		printf("%d\n",s[x][min(x,y)]);
	}
	return 0;
}

顺带提一下,当 y > x y>x y>x的时候解为 s x , x s_{x,x} sx,x,因为在 x x x个数中选 y y y个数一定选不出来。
评测记录
然而我们发现每次都要近似 2000 × 2000 2000\times2000 2000×2000的循环太浪费时间,有些时候没必要递推这么多,所以我们可以记录最大的 n n n,并按此递推(真心怕出题人毒瘤)
代码实现:

#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,x[10039],y[10039],f[2039][2039],s[2039][2039],ans,tot,pus;
int main(){
	register int i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]),ans=max(x[i],ans);
	f[1][0]=f[1][1]=1;
	for(i=2;i<=ans;i++){
		f[i][0]=1;
		for(j=1;j<=i;j++){
			f[i][j]=(f[i-1][j]+f[i-1][j-1])%m;
			if(!f[i][j]) s[i][j]=1;
			s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
		}
		s[i][i+1]=s[i][i];
	}
	for(i=1;i<=n;i++)printf("%d\n",s[x[i]][min(x[i],y[i])]);
	return 0;
}

评测记录
数据小的点就跑得飞起。
附:前缀和题解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值