题目: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)的更多相关文章

  1. 51nod 1362 搬箱子——[ 推式子+组合数计算方法 ] [ 拉格朗日插值 ]

    题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1362 方法一: 设 a 是向下走的步数. b 是向右下走的步数. c 是向下走 ...

  2. 组合数学中的常见定理&组合数的计算&取模

    组合数的性质: 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. 3-为什么很多 对 1e9+7(100000007)取模

    首先有很多题目的答案是很大的,然而出题人的本意也不是让选手写高精度或者Java,所以势必要让答案落在整型的范围内.那么怎么做到这一点呢,对一个很大的质数取模即可(自行思考为什么不是小数).那么如果您学 ...

  4. 组合数取模及Lucas定理

    引入: 组合数C(m,n)表示在m个不同的元素中取出n个元素(不要求有序),产生的方案数.定义式:C(m,n)=m!/(n!*(m-n)!)(并不会使用LaTex QAQ). 根据题目中对组合数的需要 ...

  5. [BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】

    题目链接:BZOJ - 3129 题目分析 使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1). 如果有一个限制条件是 xi >= Ai ,那么我们就可以将 ...

  6. BZOJ_2142_礼物_扩展lucas+组合数取模+CRT

    BZOJ_2142_礼物_扩展lucas+组合数取模 Description 一年一度的圣诞节快要来到了.每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物.不同的人物在小E 心目中的重要性不同 ...

  7. 组合数取模&&Lucas定理题集

    题集链接: https://cn.vjudge.net/contest/231988 解题之前请先了解组合数取模和Lucas定理 A : FZU-2020  输出组合数C(n, m) mod p (1 ...

  8. HDU 5698 大组合数取模(逆元)

    瞬间移动 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submis ...

  9. BZOJ 3656: 异或 (组合数取模 CRT)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3656 大意:经过一通推导,问题变成求\[\binom N M \mod P\],其中N,M<= ...

随机推荐

  1. Java方法存在于哪一区

    Java运行时的数据区包括:(其中前两个是线程共享的) 1.方法区(Method Area)存储已被虚拟机加载的类信息.常量.静态变量.即编译器编译后的代码等数据 2.堆(Heap)存放对象实例,几乎 ...

  2. 浅谈iOS中MVVM的架构设计与团队协作【转载】

    今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  3. redis客户端连接,最大连接数查询与设置

    ##redis客户端连接数 redis通过监听一个TCP端口或socket的方式接收来自客户端的连接, 当与客户端建立连接后,redis内部会进行如下操作:()客户端socket会被设置为非阻塞模式, ...

  4. gdb ../sysdeps/i386/elf/start.S: No such file or directory

    使用 gdb 调试的时候 输入 l 之后出现下列信息 (gdb) l 1 ../sysdeps/i386/elf/start.S: No such file or directory. in ../s ...

  5. EasyDSS RTMP流媒体解决方案之Windows服务安装方案

    Windows服务安装 EasyDSS_Solution流媒体解决方案,可以通过start一键启动.在实际应用中,我们希望可以设置成系统服务,那么下面我将会介绍,如何在windows中将流媒体解决方案 ...

  6. 源码编译mysql 5.5+ 安装过程全记录

    前言:从mysql 5.5版本开始,mysql源码安装开始使用cmake了,编译安装跟以前的版本有点不一样了. 一,安装步骤: 1.安装前准备工作 a.下载mysql源代码包,到mysql下载页面选择 ...

  7. Linq的优缺点

    优点: 1.Linq提供了不同数据源的抽象层,所以可以使用相同的语法访问不同的数据源(只要该数据源有提供程序即可) 2.Linq为底层的数据存储提供了一个强类型化的界面,可以把底层的数据作为对象来访问 ...

  8. 九度OJ 1159:坠落的蚂蚁 (模拟、排序)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1098 解决:277 题目描述: 一根长度为1米的木棒上有若干只蚂蚁在爬动.它们的速度为每秒一厘米或静止不动,方向只有两种,向左或者向右.如 ...

  9. HTML5+ Android打包证书

    HBuilder默认App云端打包默认使用的是DCloud公用证书,其信息如下: MD5: 59:20:1C:F6:58:92:02:CB:2C:DA:B2:67:52:47:21:12 SHA1:B ...

  10. Java for LeetCode 120 Triangle

    Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent n ...