题目传送门


分析

两点之间的最小距离其实是由两点高度最大值决定的,

求出长度为 \(n\) 的排列所需距离的方案数,剩下还能放的距离可以用插板法放进去。

也就是 \(\sum_{i=1}^{n^2}f_i*\binom{m-i+n}{n}\)

设 \(dp[i][j][k]\) 表示 \(1\sim i\) 被分成 \(j\) 段所需距离为 \(k\) 的方案数。

新开一段就是 \(dp[i][j+1][k+1]+=dp[i-1][j][k]*(j+1)\)(有 \(j+1\) 个位置可以选)

合并到一段开头或结尾就是 \(dp[i][j][k+i]+=dp[i-1][j][k]*(2*j)\)

合并两段就是 \(dp[i][j-1][k+(i*2-1)]+=dp[i-1][j][k]*(j-1)\)(有 \(j-1\) 个位置可以选)

最后的 \(f_k\) 就是 \(dp[n][1][k]\),时间复杂度 \(O(n^4)\)

考虑一下排列组合怎么求,由于模数不一定是质数,

我一开始以为要质因数分解,写完之后发现其实可以把 \(x+n\) 加进去之后再将 \(x\) 删掉,

删除的过程实际上直接用最大公约数删除就可以了。

这样的时间复杂度是 \(O(n^3\log m)\) 的。

其实还有一种方法是利用杨辉三角的递推公式矩阵加速递推,

然后再用杨辉三角把所有的值都求出来,虽然复杂度相同,不过常数有点大。

注意第一种方法求单个组合数实际上是 \(O(n\log m)\),但是由于 \(R-L\) 在平方范围内,所以看起来是立方的。

并且通过第一种方法(\(C(a,b)\) 的 \(b\) 固定)用杨辉三角倒推回去就可以在 \(O(n^2\log m)\) 的复杂度下得到一行的组合数。

但是用杨辉三角的方法(\(C(a,b)\) 的 \(a\) 固定)一定是三次方的,所以还是不要用矩阵乘法吧。


代码(矩阵乘法)

#include <iostream>
using namespace std;
const int N=111;
struct maix{int p[N][N];}A,ANS;
int dp[N][N*N],n,m,mod,L,R,mid,ans;
void Mo(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
maix mul(maix A,maix B,int t){
maix C;
for (int i=0;i<=t;++i)
for (int j=0;j<=n;++j){
C.p[i][j]=0;
for (int k=0;k<=n;++k)
Mo(C.p[i][j],1ll*A.p[i][k]*B.p[k][j]%mod);
}
return C;
}
int main(){
ios::sync_with_stdio(0);
cin>>n>>m>>mod,dp[1][1]=1,mid=(n+1)>>1;
L=n*(n+1)/2,R=n*(n+3)-mid*(mid+2);
if (L>m){
cout<<0;
return 0;
}
if (R>m) R=m;
for (int i=2;i<=n;++i){
for (int k=R;k;--k)
for (int j=1;j<i;++j)
if (dp[j][k]){
int t=dp[j][k];
if (j>1&&k+(i*2-1)<=R) Mo(dp[j-1][k+(i*2-1)],t*(j-1ll)%mod);
if (k+i<=R) Mo(dp[j][k+i],2ll*t*j%mod);
if (k<R) Mo(dp[j+1][k+1],t*(j+1ll)%mod);
dp[j][k]=0;
}
}
A.p[0][0]=ANS.p[0][0]=1;
for (int i=1;i<=n;++i)
A.p[i-1][i]=A.p[i][i]=1;
for (int t=m-R+n;t;t>>=1,A=mul(A,A,n))
if (t&1) ANS=mul(ANS,A,0);
for (int i=R;i>=L;--i){
if (dp[1][i]) Mo(ans,1ll*dp[1][i]*ANS.p[0][n]%mod);
for (int j=n;j;--j) Mo(ANS.p[0][j],ANS.p[0][j-1]);
}
cout<<ans;
return 0;
}

代码(插入 \(x+n\) 再删除 \(x\))

#include <iostream>
using namespace std;
const int N=111;
int dp[N][N*N],n,m,mod,L,R,mid,ans,a[N],o=1;
void Mo(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int main(){
ios::sync_with_stdio(0);
cin>>n>>m>>mod,dp[1][1]=1,mid=(n+1)>>1;
L=n*(n+1)/2,R=n*(n+3)-mid*(mid+2);
if (L>m){
cout<<0;
return 0;
}
if (R>m) R=m;
for (int i=2;i<=n;++i){
for (int k=R;k;--k)
for (int j=1;j<i;++j)
if (dp[j][k]){
int t=dp[j][k];
if (j>1&&k+(i*2-1)<=R) Mo(dp[j-1][k+(i*2-1)],t*(j-1ll)%mod);
if (k+i<=R) Mo(dp[j][k+i],2ll*t*j%mod);
if (k<R) Mo(dp[j+1][k+1],t*(j+1ll)%mod);
dp[j][k]=0;
}
}
for (int i=1;i<=n;++i) a[i]=m-R+n-i+1;
for (int i=2;i<=n;++i)
for (int j=1,x=i;j<=n&&x>1;++j){
int GCD=gcd(a[j],x);
x/=GCD,a[j]/=GCD;
}
for (int i=R;i>=L;--i){
int now=1;
for (int j=1;j<=n;++j) now=1ll*now*a[j]%mod;
if (dp[1][i]) Mo(ans,1ll*dp[1][i]*now%mod);
a[o]=m-i+n+1,o=o%n+1;
for (int j=1,x=m-i+1;j<=n&&x>1;++j){
int GCD=gcd(a[j],x);
x/=GCD,a[j]/=GCD;
}
}
cout<<ans;
return 0;
}

#排列组合,dp#LOJ 6069 「2017 山东一轮集训 Day4」塔的更多相关文章

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

    Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...

  2. Loj 6068. 「2017 山东一轮集训 Day4」棋盘

    Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...

  3. [LOJ#6068]. 「2017 山东一轮集训 Day4」棋盘[费用流]

    题意 题目链接 分析 考虑每个棋子对对应的横向纵向的极大区间的影响:记之前这个区间中的点数为 \(x\) ,那么此次多配对的数量即 \(x\) . 考虑费用流,\(S\rightarrow 横向区间 ...

  4. LOJ 6068「2017 山东一轮集训 Day4」棋盘

    题意 一个 \(n\times n\) 的棋盘上面有若干障碍物. 定义两个棋子可以互相攻击当且仅当这两个棋子的横坐标或纵坐标相等而且中间不能隔着障碍物.(可以隔棋子) 有 \(q\) 次询问,每次询问 ...

  5. Loj #6073.「2017 山东一轮集训 Day5」距离

    Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...

  6. loj6068. 「2017 山东一轮集训 Day4」棋盘 二分图,网络流

    loj6068. 「2017 山东一轮集训 Day4」棋盘 链接 https://loj.ac/problem/6068 思路 上来没头绪,后来套算法,套了个网络流 经典二分图 左边横,右边列 先重新 ...

  7. loj #6077. 「2017 山东一轮集训 Day7」逆序对

    #6077. 「2017 山东一轮集训 Day7」逆序对   题目描述 给定 n,k n, kn,k,请求出长度为 n nn 的逆序对数恰好为 k kk 的排列的个数.答案对 109+7 10 ^ 9 ...

  8. LOJ #6074. 「2017 山东一轮集训 Day6」子序列

    #6074. 「2017 山东一轮集训 Day6」子序列 链接 分析: 首先设f[i][j]为到第i个点,结尾字符是j的方案数,这个j一定是从i往前走,第一个出现的j,因为这个j可以代替掉前面所有j. ...

  9. loj#6074. 「2017 山东一轮集训 Day6」子序列(矩阵乘法 dp)

    题意 题目链接 Sol 设\(f[i][j]\)表示前\(i\)个位置中,以\(j\)为结尾的方案数. 转移的时候判断一下\(j\)是否和当前位置相同 然后发现可以用矩阵优化,可以分别求出前缀积和逆矩 ...

  10. LOJ#6075. 「2017 山东一轮集训 Day6」重建

    题目描述: 给定一个 n个点m 条边的带权无向连通图 ,以及一个大小为k 的关键点集合S .有个人要从点s走到点t,现在可以对所有边加上一个非负整数a,问最大的a,使得加上a后,满足:s到t的最短路长 ...

随机推荐

  1. kubernetes(k8s)大白学习01-kubernetes是什么?有什么用?

    kubernetes(k8s)大白基础学习-kubernetes是什么? 一.认识 Docker Docker 是什么 先来看看 Docker 的图标: 一条鲸鱼背上驮着四方形块的物品,就像一条海运船 ...

  2. Qt+QtWebApp开发笔记(六):http服务器html实现静态相对路径调用第三方js文件

    前言   前面做了一些交互,网页是直接通过html对response进行返回的,这里QtWebApp与传统的web服务器不同,传统的web服务器可以调用同级目录相对路径或者绝对路径下的js,而QtWe ...

  3. Django关于StreamingHttpResponse与FileResponse响应文件或视频的下载请求

    StreamingHttpResponse from django.http import StreamingHttpResponse StreamingHttpResponse(streaming_ ...

  4. 用容器部署Nexus 3作为Nuget和Docker的仓库

    1.准备docker-compose的配置文件 version: '3' services: nexus: image: 'sonatype/nexus3:3.42.0' container_name ...

  5. 服务网关ZUUL过滤器

    过滤器两个功能: 1.其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础: 2.过滤器功能则负责对请求的处理过程进行预干预,是实现请求校验.服务聚合等功能的基础. 有4类 ...

  6. NebulaGraph v3.3.0 发布:支持子图过滤、和大量性能优化

    NebulaGraph 3.3.0 支持了 GET SUBGRAPH 和 GetNeighbors 的点过滤.引入了大量性能优化,同时,开始对无 tag 顶点的支持默认关闭. 优化 优化了 k-hop ...

  7. Nebula Graph 的 KV 存储分离原理和性能测评

    本文首发于 Nebula Graph Community 公众号 1. 概述 过去十年,图计算无论在学术界还是工业界热度持续升高.相伴而来的是,全世界的数据正以几何级数形式增长.在这种情况下,对于数据 ...

  8. 微信小程序直播,腾讯云直播+微信小程序实现实时直播

    一:小程序代码端 小程序直播使用小程序组件 live-pusher 组件和live-player组件 首先开通直播权限 小程序开发工具内进行推流拉流都不会成功,所以需要使用两个手机进行推拉流测试: 1 ...

  9. Python使用pyecharts库构建股票交易 可交互可视化展示——基于Tushare大数据开放社区

    Python使用pyecharts库构建股票交易 可交互可视化展示--基于Tushare大数据社区 1.前言 大家好!欢迎各位访问我的博客,今天给大家分享的是 python使用pyecharts库构建 ...

  10. python 音频通道分离的源码实现

    一 前记 作为一个音频工程师,仅仅依靠鼠标点击,没有一些自己的小工具的话,肯定是不合格的了. 最近用到了一个音频通道分离的功能,这里就用python敲击了一下,这里做个备忘吧,给有需求的小伙伴抛砖引玉 ...