Loj #6069. 「2017 山东一轮集训 Day4」塔

题目描述

现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点。

塔有编号,且每座塔都有高度,对于编号为 $ i $ 座塔,其高度为 $ i $。对于一座塔,需要满足它与前面以及后面的塔的距离大于等于自身高度(不存在则没有限制)。问有多少建造方案。答案对 $ m $ 取模。

塔不要求按编号为顺序建造。

输入格式

一行三个整数 $ n, l, m $。

输出格式

输出一个整数,代表答案对 $ m $ 取模的值。

样例

样例输入

3 9 17

样例输出

15

数据范围与提示

对于 $ 10% $ 的数据,$ n \leq 10; l \leq 25 $;

对于 $ 30% $ 的数据,$ n \leq 20 $;

对于 $ 50% $ 的数据,$ n \leq 50 $;

对于 $ 70% $ 的数据,$ l \leq 105 $;

对于 $ 100% $ 的数据,$ n \leq 100; 1 \leq l \leq 10 ^ 9; 1 \leq m \leq 10 ^ 9 $。

首先我们得到一个排列\(P\),设\(S=\sum max\{P_{i-1},P_i\}\),\(S+1\)就是这个排列紧密地排在一起时的长度。还剩下了\(l-(S+1)\)个格子, 我们就将这些格子放在相邻的元素之间,方案数为\(\binom{l-S-1+n}{n}\)。

所以我们要先求出\(S=k\)的排列个数。

设\(f_{i,j,k}\)表示放了前\(i\)个数,整个排列分为了\(j\)个联通块,排列的总长度为\(k\)的方案数。因为我们设从小到大放数,所以如果一个数\(i\),它的左侧是空的或者会有另一个数,则不会对\(S\)有贡献,右侧同理。

转移的时候就考虑第\(i\)个数与多少个联通块相连(最多两个),就可以知道第\(i\)个数对\(S\)的贡献。

接下来考虑处理组合数。因为\(n\)比较小,所以对于每个\(L\),我们只保留\(\binom{L}{0..n}\)。用矩阵快速幂处理即可。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 105 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n,l;
ll mod;
int f[N][N*N],g[N][N*N];
int per[N*N]; struct matrix {
int a[105][105];
void Init() {
memset(a,0,sizeof(a));
}
}F,G; int Mod(int a) {return a<mod?a:a-mod;}
matrix operator *(const matrix &x,const matrix &y) {
static matrix tem;
tem.Init();
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)
tem.a[i][j]=Mod( tem.a[i][j]+1ll*x.a[i][k]*y.a[k][j]%mod);
return tem;
} matrix ksm(matrix g,int x) {
matrix ans;
ans.Init();
for(int i=0;i<=n;i++) ans.a[i][i]=1;
for(;x;x>>=1,g=g*g)
if(x&1) ans=ans*g;
return ans;
} int main() {
n=Get(),l=Get(),mod=Get();
int sum=0;
f[1][0]=1;
sum=2;
for(int i=2;i<=n;i++) {
for(int j=1;j<i;j++) memset(g[j],0,sizeof(g[j]));
for(int j=1;j<=i;j++) {
for(int k=0;k<=sum;k++) {
if(!f[j][k]) continue ;
g[j+1][k]=Mod(g[j+1][k]+1ll*f[j][k]*(j+1)%mod);
g[j][k+i]=Mod(g[j][k+i]+1ll*f[j][k]*j*2%mod);
if(j>1) g[j-1][k+2*i]=Mod(g[j-1][k+2*i]+1ll*f[j][k]*(j-1)%mod);
}
}
memcpy(f,g,sizeof(f));
sum+=2*i;
}
for(int i=0;i<sum;i++) per[i+1]=f[1][i];
int mx=0;
for(int i=0;i<sum;i++) if(per[i]) mx=i;
mx=min(mx,l+n);
F.a[0][0]=1;
for(int i=0;i<=n;i++) {
G.a[i][i]=1;
if(i) G.a[i-1][i]=1;
}
F=F*ksm(G,l-mx+n);
ll ans=0;
for(int i=mx;i>=0;i--) {
(ans+=1ll*per[i]*F.a[0][n])%=mod;
for(int j=n;j>0;j--) {
(F.a[0][j]+=F.a[0][j-1])%=mod;
}
}
cout<<ans;
return 0;
}

最新文章

  1. Github+Jekyll —— 创建个人免费博客(一)从零开始
  2. Android 不一样的原生分享
  3. C语言学习008:标准错误
  4. 搭建企业内部yum仓库(centos6+centos7+epel源)
  5. JQUERY 模糊选择
  6. 关于nodejs4.0 npm乱码以及离线全局安装时要注意的问题
  7. C++例题练习(1)
  8. 数据结构&mdash;&mdash;左高树
  9. springMVC + mybatis 搜索 分页等
  10. Windows 驱动发展基金会(九)内核函数
  11. 201521123087 《java程序设计》 第七周学习总结
  12. 【基础】这15种CSS居中的方式,你都用过哪几种?
  13. ESP8266 wifi 模块配置,Wechat+APP控制实现
  14. 洛谷P2234 [HNOI2002]营业额统计
  15. node获取windows pc 机器的标示
  16. PTA——猜数字游戏
  17. spring boot和mybatis入门
  18. PFX文件提取公钥私钥
  19. Python3基础 try-指定except-as reason 捕获打开一个不存在的文件的时候,会产生OSError异常的示例
  20. Windows下创建文件的权限问题

热门文章

  1. 5. CopyOnWriteArrayList 的适用场景
  2. JavaAndroid项目配置文件笔记
  3. java使用POI将数据导出放入Excel
  4. C# 动态添加类、动态添加类型、代码添加类型
  5. angular 1.2.29版本下 动态添加多个表单、 校验全部、 提交 、ng-form方案
  6. canvas-8searchLight4.html
  7. 浅析 JavaScript 中的 Function.prototype.bind() 方法
  8. BZOJ4559: [JLoi2016]成绩比较(dp 拉格朗日插值)
  9. Dynamics 365 Online-Unified User Interface
  10. 自定义View的三个构造函数