「解题报告」P9197 [JOI Open 2016] 摩天大楼
水个题。
好像是连续段 DP 模板题,但是没怎么做过连续段 DP。
连续段 DP 大致思想就是对排列的计数,可以按照某个顺序依次填入每个数,将当前填的数看做若干连续段,每次考虑合并两个连续段,新建两个连续段或拓展一个连续段,然后就容易对排列进行计数了。
这题有一个绝对值的限制,而我们可以把绝对值按照值域拆开,考虑每一段值域上有多少个数跨过这一段,即可统计贡献。那么此时我们就只需要考虑当前的连续段数了。然后直接转移即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005, P = 1000000007;
int f[2][MAXN][MAXN][2][2];
int n, l, a[MAXN];
void add(int &a, int b) {
a += b;
if (a >= P) a -= P;
}
int main() {
scanf("%d%d", &n, &l);
if (n == 1) {
printf("1\n");
return 0;
}
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
sort(a + 1, a + 1 + n);
for (int i = n; i >= 2; i--) {
a[i] -= a[i - 1];
}
a[1] = 0;
int o = 0;
f[0][0][0][0][0] = 1;
for (int i = 1; i <= n; i++) {
memset(f[o ^ 1], 0, sizeof f[o ^ 1]);
for (int j = 0; j <= n; j++) for (int k = 0; k <= l; k++) for (int x = 0; x <= 1; x++) for (int y = 0; y <= 1; y++) {
if (f[o][j][k][x][y]) {
if (k + (2 * j - x - y) * a[i] <= l) {
add(f[o ^ 1][j + 1][k + (2 * j - x - y) * a[i]][x][y], 1ll * f[o][j][k][x][y] * (j + 1 - x - y) % P);
if (!x) add(f[o ^ 1][j + 1][k + (2 * j - x - y) * a[i]][1][y], f[o][j][k][x][y]);
if (!y) add(f[o ^ 1][j + 1][k + (2 * j - x - y) * a[i]][x][1], f[o][j][k][x][y]);
if (j) {
add(f[o ^ 1][j][k + (2 * j - x - y) * a[i]][x][y], 1ll * f[o][j][k][x][y] * (2 * j - x - y) % P);
if (!x) add(f[o ^ 1][j][k + (2 * j - x - y) * a[i]][1][y], f[o][j][k][x][y]);
if (!y) add(f[o ^ 1][j][k + (2 * j - x - y) * a[i]][x][1], f[o][j][k][x][y]);
}
if (j >= 2) add(f[o ^ 1][j - 1][k + (2 * j - x - y) * a[i]][x][y], 1ll * f[o][j][k][x][y] * (j - 1) % P);
}
}
}
o ^= 1;
}
int ans = 0;
for (int i = 0; i <= l; i++) {
add(ans, f[o][1][i][1][1]);
}
printf("%d\n", ans);
return 0;
}
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话