题目: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. Oracle:创建存储过程

    1.无参存储过程 create or replace procedure test_procasv_total number(10);begin  select count(*) into v_tot ...

  2. emacs 简记

    简介 Emacs作为神的编辑器,不用介绍了吧,说点感受. 用了一段时间了,总体感觉其实Emacs是很简单的,甚至比vim还简单,因为在X环境下,打开后可以就像记事本一样使用.但是,使用Emacs的人一 ...

  3. ssh port forwarding

    SSH端口转发,总是忘记,今天记录下.端口转发有两种,一个是local一个是remote(可能还有一种dynamic,还没有研究) 贴个链接 https://www.ssh.com/ssh/tunne ...

  4. LightOJ 1068 Investigation (数位dp)

    problem=1068">http://www.lightoj.com/volume_showproblem.php?problem=1068 求出区间[A,B]内能被K整除且各位数 ...

  5. 关于ejabberd限制单点登录

    ejabberd 是对xmpp协议的完全实现,那么单纯的ejabberd是不提供该功能限制的,但是从我们的xmpp协议则可以完全的解决这个问题,我们通过jid对它进行限制,下面可以看一下jid的解释: ...

  6. C#高级编程 第十五章 反射

    (二)自定义特性 使自定义特性非常强大的因素时使用反射,代码可以读取这些元数据,使用它们在运行期间作出决策. 1.编写自定义特性 定义一个FieldName特性: [AttributeUsage(At ...

  7. Hihocoder #1602 : 本质不同的回文子串的数量 manacher + BKDRhash

    #1602 : 本质不同的回文子串的数量 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个字符串S,请统计S的所有子串中,有多少个本质不同的回文字符串? 注意如果 ...

  8. 编译安装Heartbeat常见错误

    -----------那些需要升级包还有少包的错误就不写了---------- <b>1</b>. Reusable-Cluster-Components-glue-glue- ...

  9. sql中decode()重要函数使用

    decode()函数简介: 主要作用:将查询结果翻译成其他值(即以其他形式表现出来,以下举例说明): 使用方法: Select decode(columnname,值1,翻译值1,值2,翻译值2,.. ...

  10. django url匹配过程

    ROOT_URLCONF root URLconf module urlpatterns “include” other URLconf modules chops off whatever part ...