[题解] permutation

news/2024/9/18 1:00:48/文章来源:https://www.cnblogs.com/xiaolemc/p/18374557

[题解] Permutation

image

image

image

解析

一眼 DP 或者 组合。

70pts

场上推的DP

对于 \((4,2,2)\),先把所有序列枚举出来:

\[\begin{split} 1\ \ \ 2\\ 1\ \ \ 3\\ 1\ \ \ 4\\ --\\ 2\ \ \ 3\\ 2\ \ \ 4\\ 3\ \ \ 4 \end{split} \]

可以发现,对于分割线上的部分,可以看作 \((3,1,1)\) 的所有序列每个数 \(+1\),然后前导一个 \(1\)。因为答案计算的是绝对值,所以每个数加一相当于整体抬高,不影响答案。而对于分割线下的部分,可以看作 \((3,2,2)\) 的所有序列每个数 \(+1\),同样不影响答案。令 \(f(i,j,z)\) 表示对应 \((n,k,m)\) 的答案。那么有:

\[f(i,j,z)=f(i-1,j-1,z-1)+f(i-1,j,z)+? \]

留个问号是因为,这两部分合并还可以产生贡献,为上面部分最后一个数与下面部分第一个数的差的绝对值。上面部分最后一个数为 \(i\),下面部分第一个数为 \(j+1\)。所以有:

\[\begin{split} f(i,j,z)&=f(i-1,j-1,z-1)+f(i-1,j,z)+|i-j-1|\\ \end{split} \]

\[\begin{split}f(i,i,z)&=0\\ f(i,j,1)&=i-j \end{split} \]

\(i,j,z\) 都是 \(10^6\) 级别的,空间会炸。可以发现 \(i\) 只和 \(i-1\) 有关,可以滚掉。\(j\)\(z\) 同减,并且 \(z\le j\),那么只维护最小的 \(z\) 即可。

场码
#include<bits/stdc++.h>
using namespace std;
constexpr int B = 1 << 23;
char buf[B], *p1 = buf, *p2 = buf;
#define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++)
template <typename T> inline void rd(T &x){x = 0; int f = 0; char ch = gt();for(; !isdigit(ch); ch = gt()) f ^= ch == '-';for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48);x = f ? -x : x;
}
char obuf[B], *O = obuf;
#define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++=(ch))
template <typename T> inline void wt(T x){if(x < 0) pt('-'), x = -x;if(x >= 10) wt(x / 10); pt(x % 10 ^ 48);
}
#define fw fwrite(obuf, 1, O - obuf, stdout)
#define ll long long
#define ull unsigned long long
constexpr int N = 1e6 + 5, M = 1e9 + 7;
int n, k, m, f[2][N], lst, now = 1;
int main(){
//	freopen("perm.in", "r", stdin);
//	freopen("perm.out", "w", stdout);rd(n), rd(k), rd(m);if(n == k) wt(m);else {int tmp = k - m;for(int i=tmp; i<=n; ++i){for(int j=max(1, m+i-n); j+tmp<=i; ++j){if(j == 1) f[now][j] = i - (j + tmp);else if(i == j + tmp) f[now][j] = 0;else f[now][j] = ((ll)f[lst][j] + (ll)f[lst][j-1] + abs(i - j - tmp - 1)) % M;}for(int j=max(i, m+i-1-n); j+tmp<=i-1; ++j) f[lst][j] = 0;now ^= 1, lst ^= 1;} wt(f[lst][m]);} return fw, 0;
}

另一种 DP 思路

先打表找规律。打出 \(8\) 以内的表可以发现,答案只和 \(n-m\)\(k\) 相关,那么把 \(k\) 设为横坐标,\(n-m\) 设为纵坐标,于是有:

\[\begin{matrix} 1&2&3&4&5&\cdots\\ 1&4&9&16&25&\cdots\\ 1&6&17&36&65&\cdots\\ 1&8&27&66&135&\cdots\\ 1&10&39&108&247&\cdots\\ 1&12&53&164&415&\cdots\\ \vdots&\vdots&\vdots&\vdots&\vdots&\ddots \end{matrix} \]

\(f(i,j)\) 表示图上对应横坐标与纵坐标的值。于是有:

\[f(i,j)=f(i-1,j)+f(i,j-1)+j \]

注意边界判断:

\[\begin{split} f(1,i)=i+1\\ f(i,0)=0 \end{split} \]

答案即为:

\[f(k,n-m-1) \]

于是 \(\mathcal{O}(n^2)\) 递推即可。

100pts

考虑优化刚才的那个 DP,可以发现,对于点 \((i,j)\) 的答案可以看作是从这个点一直走到到点 \((0,0)\) 的所有路径的权值总和。这个东西可以用组合求解。可以枚举每一个点 \((i,j)\),一共有 \(\binom{i+j}{i}\) 条路径经过这一点。对于边界 \(i=1\) 需要特判一下,看作权值为 \(1\)。于是可以列出式子:

\[\sum_{i=0}^{m-2}\sum_{j=0}^{n}(n-j)\binom{i+j}{j}+\sum_{j=0}^{n}\binom{m-1+j}{j} \]

但是这个式子是 \(\mathcal{O}(n^2)\) 的。所以考虑优化。有一个公式:

\[\sum_{i=m}^{n}\binom{a+i}{i}=\binom{a+n+1}{n}-\binom{a+m}{m-1} \]

用上面的式子套公式即可优化到一维:

\[\sum_{j=0}^{n}\binom{j+m-2}{j+1}+\sum_{j=0}^{n}\binom{m-1+j}{j} \]

复杂度 \(\mathcal{O}(n)\)

code
#include<bits/stdc++.h>
using namespace std;
constexpr int N = 1e6 + 5, M = 1e9 + 7;
int n, m, k, ans, p[N<<1], inv[N<<1];
#define ll long long
inline int qpow(int a, int k){int as = 1;while(k){if(k & 1) as = (ll)as * a % M;a = (ll)a * a % M; k >>= 1;} return as;
}
inline int C(int a, int b){return (ll)p[a] * inv[b] % M * inv[a-b] % M;
}
int main(){freopen("perm.in", "r", stdin);freopen("perm.out", "w", stdout);ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin>>n>>m>>k;if(n == m) return cout<<m, 0;n -= m + 1;p[0] = 1; for(int i=1; i<=n+m; ++i) p[i] = (ll)p[i-1] * i % M;inv[n+m] = qpow(p[n+m], M-2); for(int i=n+m-1; i>=0; --i) inv[i] = (ll)inv[i+1] * (i+1) % M;if(k >= 2) for(int j=0; j<=n; ++j) ans = ((ll)ans + (ll)(n-j) * C(j+k-1, j+1) % M) % M;for(int j=0; j<=n; ++j) ans = ((ll)ans + (ll)C(k+j-1, j)) % M;return cout<<ans, 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/785545.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

金蝶云星空一个业务对象下存在两个扩展时处理方案

问题描述: 出现这种情况一般是在A账套删除了一个扩展,然后又新建了一个扩展,此时登录B账套就会出现这种情况。如果是同一台机器,那未引入对象即是被删除的,未加载对象则是刚扩展的。解决方案: 此时未加载不建议加载,先引入需要删除的对象,然后删除,最后再加载。

信息学奥赛初赛天天练-72-NOIP2016普及组-基础题3-无向图、简单无向图、自环、平行边、顶点的度、握手定理、递归

NOIP 2016 普及组 基础题3 5 以下不是存储设备的是( ) A 光盘 B 磁盘 C 固态硬盘 D 鼠标 6 如果开始时计算机处于小写输入状态,现在有一只小老鼠反复按照 CapsLock、 字母键 A、字母键 S、字母键 D、字母键 F 的顺序循环按键,即 CapsLock、A、S、D、F、CapsLock、A、S…

JavaSE基础知识分享(十三)

今天继续讲Java中的网络编程的知识!写在前面 今天继续讲Java中的网络编程的知识! 网络编程 概述 计算机网络 计算机网络是将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统、网络管理软件及网络通信协议的管理和协调下,实现资源…

金箱子

我们设 \(f[i][j]\)表示目前前 \(i\) 个宝箱的期望贡献的 \(j\) 次方。 根据题意可得 $f[i][k]=(f[i-1][1]+a[i])^k \cdot p[i]+(f[i-1][1]+b[i])^k \cdot (1-p[i]) $ 这个式子很难处理,不妨用二项式定理优化 优化后式子则为:\(f[i][k]= \sum _{j=0}^{k} C_{k}^{j} \cdot f[i-…

【PHP安全】demo3:最简单的php代码加密方法

当我们说 "PHP代码加密",我理解的是将 PHP 代码进行混淆或加密,以防止源代码被他人轻易阅读或修改。 这种需求通常用于保护商业秘密或加强代码安全性。常见的工具是使用专业的编译器和加密工具。 然而,请注意,完全保护代码是不可能的,因为最终服务器仍然需要能够…

博客园-awescnb插件-geek皮肤优化--公众号卡片

简介 博客园-awescnb插件-geek皮肤暂不支持配置展示公众号二维码,此文章目的使用手动注入方式自定义实现公众号卡片效果 效果展示公众号卡片动态效果鼠标移入前为公众号指引页 鼠标移入后显示公众号二维码 切换动画为动态反转首页展示实现在博客日历元素blog-calendar前插入自…

Flannel Wireguard 模式

Flannel Wireguard 模式Flannel WireGuard 模式 一、环境信息主机 IPubuntu 172.16.94.141软件 版本docker 26.1.4helm v3.15.0-rc.2kind 0.18.0clab 0.54.2kubernetes 1.23.4ubuntu os Ubuntu 20.04.6 LTSkernel 5.11.5 内核升级文档二、安装服务 kind 配置文件信息 $ cat ins…

统一多层网关好处多,阿里云云原生 API 网关打造全能型网关

本文整理自阿里云云原生 API 网关的公测直播,分享了作为一款全能型网关【云原生 API 网关】是如何帮助企业落地统一网关架构的。作者:问思、望宸 网关承载了业务开发和后端运维的诸多需求,例如路由管理、流量调度、API 管理、入口安全管理等,另外网关侧也需要结合服务治理来…

Kubernetes: client-go 源码剖析(一)

kubernetes:client-go 系列文章:Kubernetes: client-go 源码剖析(一) Kubernetes: client-go 源码剖析(二)0. 前言 在看 kube-scheduler 组件的过程中遇到了 kube-scheduler 对于 client-go 的调用,泛泛的理解调用过程总有种隔靴搔痒的感觉,于是调转头先把 client-go 理…

python03-标准库 第三方库-pathlib模块

python标准库:Python自带的一组模块和库,这些模块和库提供了Python编程所需的基础功能和工具 https://docs.python.org/zh-cn/3/library/index.html?eqid=8ca0b3ea000067990000000264800802Python包索引:即PyPI(Python Package Index),是一个仓库,存放了许多可以通过pi…

企业微信如何远程打卡,免费

现在一些定位软件不好用或者要收费,那么如何能够很好的免费实现远程打开呢? 首先需要一个不用的旧手机,一直放在公司里,然后拿自己常用手机远程操作来实现,具体步骤如下:旧手机需要打开开发者模式,然后打开屏幕常亮保证不会锁屏;公司电脑下载scrcpy用来操作连接的旧手机…