51Nod 1362 搬箱子 —— 组合数(非质数取模) (差分TLE)
题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1362
首先,\( f[i][j] \) 是一个 \( i \) 次多项式;
如果考虑差分,用一个列向量维护 0 次差分到 \( n \) 次差分即可,在第 \( n \) 次上差分数组已经是一个常数;
最后一行再维护一个 0 次差分的前缀和,0 次差分其实就是答案;
为了预处理 0 位置上的各次差分值,一开始先 n^2 求出 \( f[0][0] \) 到 \( f[n][n] \),差分一下得到初始矩阵;
转移就是本层加上下一层的差分值,得到本层的下一个位置,\( n \) 次的差分值不变;
需要注意快速幂时,子函数是不能传超过大约 500*500 的数组的,所以把矩阵开成全局的;
可惜这样是 \( n^{3}logm \),会TLE;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=;
int n,m,f[xn][xn],d[xn][xn],P;
int upt(int x){while(x>=P)x-=P; while(x<)x+=P; return x;}
struct N{
int a[xn][xn];
N(){memset(a,,sizeof a);}
void init(){for(int i=;i<=n+;i++)a[i][i]=;}
void clr(){memset(a,,sizeof a);}
N operator * (const N &y) const
{
N ret;
for(int i=;i<=n+;i++)
for(int k=;k<=n+;k++)
for(int j=;j<=n+;j++)
ret.a[i][j]=upt(ret.a[i][j]+(ll)a[i][k]*y.a[k][j]%P);
return ret;
}
}a,g,t;
void print(N a)
{
for(int i=;i<=n+;i++,puts(""))
for(int j=;j<=n+;j++)printf("%d",a.a[i][j]);
}
void pw(int b)//
{
t.init();
for(;b;b>>=,g=g*g)
if(b&)t=t*g;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&P)!=EOF)
{
memset(f,,sizeof f);
memset(d,,sizeof d);
f[][]=;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
{
if(i)f[i][j]=upt(f[i][j]+f[i-][j]);
if(j)f[i][j]=upt(f[i][j]+f[i][j-]);
if(i&&j)f[i][j]=upt(f[i][j]+f[i-][j-]);
}
for(int j=;j<=n;j++)d[][j]=f[n][j];
for(int i=;i<=n;i++)
for(int j=;j<=n-i;j++)d[i][j]=upt(d[i-][j+]-d[i-][j]);
a.clr(); g.clr(); t.clr();
for(int i=;i<=n;i++)a.a[i][]=d[i][];
for(int i=;i<n;i++)g.a[i][i]=g.a[i][i+]=;
g.a[n][n]=;
g.a[n+][]=g.a[n+][n+]=;
pw(m);
int sum=;
for(int i=;i<=n+;i++)
sum=upt(sum+(ll)t.a[][i]*a.a[i][]%P),
sum=upt(sum+(ll)t.a[n+][i]*a.a[i][]%P);
printf("%d\n",sum);
}
return ;
}
差分+矩阵快速幂
其实可以考虑组合数:
因为斜着走既向下又向右,不好判断,所以不妨枚举斜着走了几格,假设 \( n<=m \),得到
\( ans = \sum\limits_{j=0}^{m} \sum\limits_{i=0}^{n} C_{i+j}^{i} * C_{j}^{n-i} \)
即 \( ans = \sum\limits_{j=0}^{m} \sum\limits_{i=0}^{n} C_{n}^{i} * C_{i+j}^{n} \),或者其实可以直接写出这个式子;
然后 \( ans = \sum\limits_{i=0}^{n} C_{n}^{i} * \sum\limits_{j=0}^{m} C_{i+j}^{n} \)
\( ans = \sum\limits_{i=0}^{n} C_{n}^{i} * C_{i+m+1}^{n+1} \)
于是求组合数即可;
但模数不是质数,没有逆元;
可以像扩展 Lucas 的做法一样,提取出模数的质因子,剩余的部分就和模数互质,可以用 exgcd 求逆元;
质因子的部分直接把次数加减即可。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=,xm=;
int n,m,mod,p[xm],cnt,t[xm];
void div(int x)
{
for(int i=;i*i<=x;i++)
{
if(x%i)continue;
p[++cnt]=i;
while(x%i==)x/=i;
}
if(x>)p[++cnt]=x;
}
ll pw(ll a,int b)
{
ll ret=;
for(;b;b>>=,a=(a*a)%mod)if(b&)ret=(ret*a)%mod;
return ret;
}
void exgcd(int a,int b,int &x,int &y)
{
if(!b){x=; y=; return;}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
int inv(int a,int b){int x,y; exgcd(a,b,x,y); return (x%b+b)%b;}//%b
int get(int x,int tp)
{
for(int i=;i<=cnt;i++)
{
if(x%p[i])continue;
int cnt=;
while(x%p[i]==)cnt++,x/=p[i];
t[i]+=cnt*tp;
}
return x;
}
int C(int n,int m)
{
if(n<m)return ;//!!
if(m==)return ;
memset(t,,sizeof t);
int ret=;
for(int i=n-m+;i<=n;i++)
ret=(ll)ret*get(i,)%mod;
for(int i=;i<=m;i++)
ret=(ll)ret*inv(get(i,-),mod)%mod;
for(int i=;i<=cnt;i++)ret=(ll)ret*pw(p[i],t[i])%mod;
return ret;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&mod)==)
{
cnt=; div(mod); int ans=;
for(int i=;i<=n;i++)
ans=(ans+(ll)C(n,i)*C(i+m+,n+))%mod;
printf("%d\n",ans);
}
return ;
}
51Nod 1362 搬箱子 —— 组合数(非质数取模) (差分TLE)的更多相关文章
- 51nod 1362 搬箱子——[ 推式子+组合数计算方法 ] [ 拉格朗日插值 ]
题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1362 方法一: 设 a 是向下走的步数. b 是向右下走的步数. c 是向下走 ...
- 组合数学中的常见定理&组合数的计算&取模
组合数的性质: C(n,m)=C(n,n-m); C(n,m)=n!/(m!(n-m)!); 组合数的递推公式: C(n,m)= C(n-1,m-1)+C(n-1,m); 组合数一般数值较大,题目会 ...
- 3-为什么很多 对 1e9+7(100000007)取模
首先有很多题目的答案是很大的,然而出题人的本意也不是让选手写高精度或者Java,所以势必要让答案落在整型的范围内.那么怎么做到这一点呢,对一个很大的质数取模即可(自行思考为什么不是小数).那么如果您学 ...
- 组合数取模及Lucas定理
引入: 组合数C(m,n)表示在m个不同的元素中取出n个元素(不要求有序),产生的方案数.定义式:C(m,n)=m!/(n!*(m-n)!)(并不会使用LaTex QAQ). 根据题目中对组合数的需要 ...
- [BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】
题目链接:BZOJ - 3129 题目分析 使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1). 如果有一个限制条件是 xi >= Ai ,那么我们就可以将 ...
- BZOJ_2142_礼物_扩展lucas+组合数取模+CRT
BZOJ_2142_礼物_扩展lucas+组合数取模 Description 一年一度的圣诞节快要来到了.每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物.不同的人物在小E 心目中的重要性不同 ...
- 组合数取模&&Lucas定理题集
题集链接: https://cn.vjudge.net/contest/231988 解题之前请先了解组合数取模和Lucas定理 A : FZU-2020 输出组合数C(n, m) mod p (1 ...
- HDU 5698 大组合数取模(逆元)
瞬间移动 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submis ...
- BZOJ 3656: 异或 (组合数取模 CRT)
http://www.lydsy.com/JudgeOnline/problem.php?id=3656 大意:经过一通推导,问题变成求\[\binom N M \mod P\],其中N,M<= ...
随机推荐
- jQuery Validate(一)
jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单. 但是在学习的过程中,我也遇到了疑惑,网上的很多例子貌似都是依赖jquery.metadata.js这个库, ...
- 循序渐进学Python 1 安装与入门
1 安装 2 使用 2.1 运行程序 3 艺搜参考 by 2013年10月16日 安装 Windows安装版,源码,帮助文档: 使用 打开开始菜单中的Python GUI启动Python解释器: 启动 ...
- Oracle -- 连接每行的内容
select wm_concat(message) from ( select rownum no, 'Case ''' || code || '''' || '' || chr(10) ...
- tao.opengl+C#绘制三维模型
一.tao.Opengl技术简介 Opengl是一种C风格的图形库,即opengl中没有类和对象,只有大量的函数.Opengl在内部就是一个状态机,利用不同的函数来修改opengl状态机的状态,以达到 ...
- os引导程序boot从扇区拷贝os加载程序loader文件到内存(boot copy kernel to mem in the same method)
[0]README 0.1) 本代码旨在演示 在boot 代码中,如何 通过 loader文件所在根目录条目 找出该文件的 在 软盘所有全局扇区号(簇号),并执行内存中的 loader 代码: 0.2 ...
- IT痴汉的工作现状10-Sprint Planning
这是我们的第四个Sprint了.因为上一个迭代周期的失利,Leader群发邮件这样描写叙述道:"对任务的乐观预计,导致Sprint 3没有如期完毕. 我们须要在这次Sprint计划中细致评估 ...
- 按模板导出Excel
说明:开发环境 vs2012 asp.net mvc4 c# 注意:Excel模板有多个sheet页,导出Excel的时候,同时给多个sheet页填充数据 1.项目结构 3.Excel模板(注意she ...
- 通过主机名来获取一个ip对象
//通过名称(ip字符串or主机名)来获取一个ip对象. InetAddress ip = InetAddress.getByName("www.baidu.com");//jav ...
- ABAP-创建物料主数据
CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA' *&------------------------------------------------------- ...
- sap 图标查看
showicon这个程序很不错,可以显示SAP里所有的ICON(图标). 用事务码SE38直接运行程序:showicon 即可. 显示列表之后,双击任何一个图标可以显示出每一个图标的详细信息.