loj2743「JOI Open 2016」摩天大楼
前言
此题使用的 dp 我称为线头 dp (不喜轻喷,主要是为了区分插头 dp),在我很久以前就有这个想法,不过一直没有找到题目,最近找到了,于是决定写这篇题解。
题目简意
给一个长度为
题解
为了方便,先将
考虑把题目里限制的式子
举个例子,设
考虑对这条路线 dp 。先不考虑最终路线的端点,可以发现路线的每一个纵截面形如若干个并列的线头,并且我们只需要关心线头的数量。于是我们像扫描线一样,从左到右对线头的形态 dp ,设
(一个线头长这样,中间的折线省略了路线的形态)
容易发现,每个
- 新增一个线头
- 合并两个线头
- 不改变
每种情况在原排列上其实表现为 (这里写出来帮助理解)
或
这三种情况的转移都是容易的,转移式在最后加上端点限制后一起写。
现在考虑最终路径端点的情况:首先可以发现,起点的线头一定在最上面,终点的线头一定在最下面,并且它们本质一样,所以这里只讨论起点。起点的形态显然只有两种,往左走和往右走。
往左走的情况可以转化成把最上面的线头的上端点封住。
而往右走的情况可以转化成在上面添加一个虚线头后,把虚线头的上端点封住。
自然地,我们需要在状态里加一维
现在的状态为
转移:
-
新建一个线头:
解释两个地方:
- 总共有
个线头,每个线头两个端点,但有 个端点被封了,所以路径延长的总数是 ,下面的同理。 - 关于转移系数
即是新建的线头可以插在任意两个相邻的线头中间或者最上面和最下面,总共 个可以插的位置,但每确定一个边界会少一个可以插的位置。
- 总共有
-
合并两个线头:
显然需要至少两个线头才能合并,选择相邻两个线头合并,所以总共有
种方式。 -
不改变:
对线头的形态不产生影响,它只需要放在 个端点处的其中一个即可。 -
作为其中一个边界:
-
往左走:
-
往右走:
-
边界:
答案:
特判:
时间复杂度:
代码
#include <bits/stdc++.h>
#define re(i, x, y) for (int i = (x); i < (y); ++i)
#define rep(i, x, y) for (int i = (x); i <= (y); ++i)
#define repp(i, x, y, d) for (int i = (x); i <= (y); i += (d))
#define reep(i, x, y) for (int i = (x); i <= (y); i <<= 1)
#define pe(i, x, y) for (int i = (x) - 1; i >= (y); --i)
#define per(i, x, y) for (int i = (x); i >= (y); --i)
#define rep_e(i, u) for (int i = head[(u)]; i; i = e[i].nxt)
#define vi vector<int>
#define vL vector<LL>
#define vii vector<pii>
#define viL vector<piL>
#define vLi vector<pLi>
#define vLL vector<pLL>
#define eb emplace_back
#define pb pop_back
#define mp make_pair
#define pii pair<int, int>
#define piL pair<int, LL>
#define pLi pair<LL, int>
#define pLL pair<LL, LL>
#define lowbit(x) ((x) & (-(x)))
#define fi first
#define se second
#define all(x) x.begin(), x.end()
#define debug(x) cout << #x << " = " << x << endl
using namespace std;
typedef unsigned int ui;
typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
#define getchar() (SB == TB && (TB = (SB = BB) + fread(BB, 1, 1 << 16, stdin), SB == TB) ? EOF : *SB++)
char BB[1 << 16], *SB = BB, *TB = BB;
template<typename T> void read(T &n) {
T w = 1;
n = 0;
char ch = getchar();
for ( ; !isdigit(ch); ch = getchar()) {
if (ch == '-') {
w = -1;
}
}
for ( ; isdigit(ch); ch = getchar()) {
n = n * 10 + (ch & 15);
}
n *= w;
}
template<typename T> void chkmn(T &a, const T &b) {
a = a > b ? b : a;
}
template<typename T> void chkmx(T &a, const T &b) {
a = a < b ? b : a;
}
const int MOD = 1e9 + 7;
int adt(const LL &a) {
return (a % MOD + MOD) % MOD;
}
int inc(const int &a, const int &b) {
return a + b >= MOD ? a + b - MOD : a + b;
}
int dec(const int &a, const int &b) {
return a - b < 0 ? a - b + MOD : a - b;
}
int mul(const int &a, const int &b) {
return 1LL * a * b % MOD;
}
int sqr(const int &a) {
return 1LL * a * a % MOD;
}
void Adt(LL &a) {
a = (a % MOD + MOD) % MOD;
}
void Inc(int &a, const int &b) {
a = a + b >= MOD ? a + b - MOD : a + b;
}
void Dec(int &a, const int &b) {
a = a - b < 0 ? a - b + MOD : a - b;
}
void Mul(int &a, const int &b) {
a = 1LL * a * b % MOD;
}
void Sqr(int &a) {
a = 1LL * a * a % MOD;
}
int fsp(int a, int x = MOD - 2) {
int res = 1;
for ( ; x; x >>= 1, Sqr(a)) {
if (x & 1) {
Mul(res, a);
}
}
return res;
}
int n, L;
int a[105], f[2][105][1005][3];
int main() {
#ifdef sword
freopen("test.in", "r", stdin);
#endif
read(n), read(L);
rep(i, 1, n) {
read(a[i]);
}
if (n == 1) {
puts("1");
return 0;
}
sort(a + 1, a + n + 1);
int now = 1, las = 0;
f[1][0][0][0] = 1;
rep(i, 1, n) {
swap(now, las);
memset(f[now], 0, sizeof(f[now]));
rep(j, 0, n) {
rep(k, 0, L) {
rep(t, 0, 2 * (j != 0)) {
int nk = k + (a[i] - a[i - 1]) * (j * 2 - t);
if (nk <= L && f[las][j][k][t]) {
const int x = f[las][j][k][t];
if (t < 2) {
if (j) {
Inc(f[now][j][nk][t + 1], mul(2 - t, x));
}
Inc(f[now][j + 1][nk][t + 1], mul(2 - t, x));
}
Inc(f[now][j][nk][t], mul(j * 2 - t, x));
Inc(f[now][j + 1][nk][t], mul(j + 1 - t, x));
if (j) {
Inc(f[now][j - 1][nk][t], mul(j - 1, x));
}
}
}
}
}
}
int ans = 0;
rep(k, 0, L) {
Inc(ans, f[now][1][k][2]);
}
printf("%d\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】