博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[NOIp提高组2016]组合数问题
阅读量:5163 次
发布时间:2019-06-13

本文共 1220 字,大约阅读时间需要 4 分钟。

题目大意

求在 \(0 \leq i \leq n\)\(0 \leq j \leq min(i,m)\) 中组合数\(C_i^j\)是k的倍数的个数

\(t\)次询问\(n\)\(m\)\(1 \leq t \leq 10^4,1 \leq n,m \leq 2000\)

解题思路

看到数据范围,好像直接预处理组合数对k取模是不错的选择

但是直接套用公式

\[ C_n^m=\frac{n!}{m!(n-m)!} \]

是不可行的,难以判断是否有因数k

所以,我们可以选用C的另一个递推式

\[ C_n^m=C_{n-1}^m+C_{n-1}^{m-1} \]

边界\(C_i^0=1\)

然后就可以\(O(nm)\)预处理所有组合数对k取模的值

这还不够,如果就此为止复杂度仍然可以在询问时爆炸

我们需要应用矩阵前缀和的技巧(容斥原理)

\(sum_{i,j}\)表示询问i,j的答案(所有\(C_u^v,0<=u<=i,0<=v<=j\)中被k整除的组合数的个数)

那么我们有如下递推式:

\[ sum_{i,j}=sum_{i-1,j}+sum_{i,j-1}-sum_{i-1,j-1}+[C_{i,j}==0] \]

询问\(n,m\)的答案就是\(sum_{n,m}\)

#include
#include
int t,k,n,m;char C[3000][3000];int sum[3000][3000];int main(){ scanf("%d%d",&t,&k); for (int i=0;i<=2000;i++) C[i][0]=1; for (int i=1;i<=2000;i++) for (int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%k; for (int i=0;i<=2000;i++) for (int j=0;j<=2000;j++){ sum[i][j]=((C[i][j]==0)&&(j<=i)); if (i) sum[i][j]+=sum[i-1][j]; if (j) sum[i][j]+=sum[i][j-1]; if (i&&j) sum[i][j]-=sum[i-1][j-1]; } for (int i=1;i<=t;i++){ scanf("%d%d",&n,&m); printf("%d\n",sum[n][m]); }}

转载于:https://www.cnblogs.com/ytxytx/p/9496202.html

你可能感兴趣的文章
go条件语句
查看>>
css使用的三种方式
查看>>
C#中Const和Readonly的区别
查看>>
Noip2016day2 组合数问题problem
查看>>
django中widget小部件
查看>>
训练记录
查看>>
使用 Windows Live ID 登录 Windows 8---------互联网时代的云端革命
查看>>
横屏模式注意点
查看>>
虚拟机对网卡的设置
查看>>
after()和inserAfter(),before()和inserBefore()区别
查看>>
JDBC——释放资源的代码
查看>>
bootstrap模态框垂直居中
查看>>
用数据管理过程(3)——可预测级别的量化管理(麦当劳的管理方式)
查看>>
DataGridView的Validating事件注册后删除操作的处理
查看>>
我的IOS学习历程-第七天
查看>>
json的两种表示结构(对象和数组).。
查看>>
iOS Undefined symbols for architecture xxx问题的总结
查看>>
bzoj 3685: 普通van Emde Boas树
查看>>
关于线程池,那些你还不知道的事
查看>>
二分类问题F-score评判指标(转载)
查看>>