P5469 [NOI2019] 机器人 题解

Description

小 R 喜欢研究机器人。

最近,小 R 新研制出了两种机器人,分别是 P 型机器人和 Q 型机器人。现在他要测试这两种机器人的移动能力,测试在从左到右排成一排的 nn 个柱子上进行,柱子用 1n1 - n 依次编号,ii 号柱子的高度为一个正整数 hih_i。机器人只能在相邻柱子间移动,即:若机器人当前在 ii 号柱子上,它只能尝试移动到 i1i - 1 号和 i+1i + 1 号柱子上。

每次测试,小 R 会选取一个起点 ss,并将两种机器人均放置在 ss 号柱子上。随后它们会按自己的规则移动。

P 型机器人会一直向左移动,但它无法移动到比起点 ss 更高的柱子上。更具体地,P 型机器人在 l(ls)l (l \leq s) 号柱子停止移动,当且仅当下列两个条件均成立:

  • l=1l = 1hl1>hsh_{l-1} > hs

  • 对于满足 ljsl \leq j \leq sjj,有 hjhsh_j \leq h_s

Q 型机器人会一直向右移动,但它只能移动到比起点 ss 更低的柱子上。更具体地,Q 型机器人在 r(rs)r (r \geq s) 号柱子停止移动,当且仅当下列两个条件均成立:

  • r=nr = nhr+1hsh_{r+1} \geq h_s

  • 对于满足 s<jrs < j \leq rjj,有 hj<hsh_j < h_s

现在,小 R 可以设置每根柱子的高度,ii 号柱子可选择的高度范围为 [Ai,Bi][A_i, B_i],即 AihiBiA_i \leq h_i \leq B_i。小 R 希望无论测试的起点 ss 选在哪里,两种机器人移动过的柱子数量的差的绝对值都小于等于 22。他想知道有多少种柱子高度的设置方案满足要求,小 R 认为两种方案不同当且仅当存在一个 kk,使得两种方案中 kk 号柱子的高度不同。请你告诉他满足要求的方案数模 109+710^9 + 7 后的结果。

1n3001 \leq n \leq 300 , 1AiBi1091 \leq A_i \leq B_i \leq 10^9

Solution

不妨设当前要求区间 [l,r][l,r] 的方案,最高的柱子的位置为 pp(如果有相同的取最右边的那个),那么这个柱子一定满足 (pl)(rp)2|(p-l)-(r-p)|\leq 2,并且这个柱子将整个序列划分成了两个互相没有关系的连续子序列,这样就可以 dp 了。

fl,r,kf_{l,r,k} 表示区间 [l,r][l,r] 的最大值不超过 kk 的方案数,可以得到转移:

fl,r,k=fl,r,k1+[(il)(ri)2]fl,i1,kfi+1,r,k1f_{l,r,k}=f_{l,r,k-1}+\sum_{\left [\left|(i-l)-(r-i)\right|\leq 2\right]}{f_{l,i-1,k}f_{i+1,r,k-1}}

容易发现转移点是 O(1)O(1) 级别的,所以时间复杂度为:O(n2V)O(n^2V)


考虑优化。

注意到我们只需要求 [1,n][1,n] 的答案,所以在求解 dp 的过程中很多状态都是无意义的,暴搜发现用到的区间最多 20472047 个,只需要对这些区间 dp 即可。

设有 mm 个用到的区间,时间复杂度就优化为了 O(mV)O(mV)


但这样还不够,现在需要把时间复杂度中的值域去掉。

考虑之前为什么需要枚举值域,这是因为对于不同的最大值,包含它的柱子是不一样的,就会导致转移方程不一样。

注意到不同的转移只有 O(n)O(n) 种,所以可以离散化然后对于每个转移一样的连续段 [L,R][L,R] 进行转移。

修改状态为 fi,jf_{i,j} 表示第 ii 个区间,最大值不超过 L+j1L+j-1 的方案数。可以得到转移:

fi,j=fi,j1+knextifxk,jfyk,j1f_{i,j}=f_{i,j-1}+\sum_{k\in \text{next}_i}{f_{x_k,j}f_{y_k,j-1}}

由于每个 fi,jf_{i,j} 的转移式和 fi,j1f_{i,j-1} 的转移式除了第一维是一样的,所以 fi,jf_{i,j} 可以表示为有关 jjRiLi+1R_i-L_i+1 次多项式。证明就考虑归纳法,如果 fi,0,fi,1fi,j1f_{i,0},f_{i,1}\ldots f_{i,j-1} 的多项式系数一样,那么 fi,jf_{i,j} 由于与之前这 jj 个 dp 的转移是一模一样的,所以其系数也和这些一样。

所以对于每个 fif_{i} 只需要维护 fi,0,fi,1,fi,RiLi+1f_{i,0},f_{i,1},\ldots f_{i,R_i-L_i+1} 的值,就可以插值求出 fi,RL+1f_{i,R-L+1}

时间复杂度:O(n2m)O(n^2m)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 305, kMaxM = 3e3 + 5, kMod = 1e9 + 7;

int n, m, cnt;
int l[kMaxN], r[kMaxN], unq[kMaxN * 2], id[kMaxN][kMaxN];
int f[kMaxM][kMaxN], g[kMaxM], fac[kMaxN], ifac[kMaxN], inv[kMaxN];
std::pair<int, int> seg[kMaxM];
std::vector<std::tuple<int, int, int>> nxt[kMaxM];

constexpr int qpow(int bs, int64_t idx = kMod - 2) {
int ret = 1;
for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
if (idx & 1)
ret = (int64_t)ret * bs % kMod;
return ret;
}

inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

void dfs(int l, int r) {
static bool vis[kMaxN][kMaxN];
if (l > r || vis[l][r]) return;
seg[++cnt] = {l, r}, vis[l][r] = 1;
for (int i = l; i <= r; ++i) {
if (abs(i - l - (r - i)) <= 2) {
dfs(l, i - 1), dfs(i + 1, r);
}
}
}

void gettrans() {
dfs(1, n);
std::sort(seg + 1, seg + 1 + cnt, [&] (std::pair<int, int> &p1, std::pair<int, int> &p2) { return p1.second - p1.first > p2.second - p2.first; });
for (int i = 1; i <= cnt; ++i) id[seg[i].first][seg[i].second] = i;
for (int i = 1; i <= cnt; ++i) {
auto [l, r] = seg[i];
for (int j = l; j <= r; ++j)
if (abs(j - l - (r - j)) <= 2)
nxt[i].emplace_back(j, id[l][j - 1], id[j + 1][r]);
}
}

int getinv(int x) { return x > 0 ? inv[x] : sub(0, inv[-x]); }

void discrete() {
std::sort(unq + 1, unq + 1 + m);
m = std::unique(unq + 1, unq + 1 + m) - (unq + 1);
fac[0] = ifac[0] = 1;
for (int i = 1; i <= n + 3; ++i) {
inv[i] = qpow(i);
fac[i] = 1ll * i * fac[i - 1] % kMod;
ifac[i] = 1ll * inv[i] * ifac[i - 1] % kMod;
}
}

void prework() {
for (int i = 1; i <= n; ++i) unq[++m] = l[i], unq[++m] = r[i] + 1;
discrete(), gettrans();
}

int getval(int *x, int n, int k) {
static int pre[kMaxN], suf[kMaxN];
int ans = 0;
pre[0] = suf[n + 1] = 1;
for (int i = 1; i <= n; ++i) pre[i] = 1ll * pre[i - 1] * sub(k, i) % kMod;
for (int i = n; i; --i) suf[i] = 1ll * suf[i + 1] * sub(k, i) % kMod;
for (int i = 1; i <= n; ++i) {
int coef = 1ll * pre[i - 1] * suf[i + 1] % kMod * ifac[i - 1] % kMod * ifac[n - i] % kMod;
if ((n - i) & 1) coef = sub(0, coef);
inc(ans, 1ll * coef * x[i] % kMod);
}
return ans;
}

void solve(int L, int R) {
for (int i = cnt; i; --i) {
for (int j = 1; j <= n + 3; ++j) {
f[i][j] = f[i][j - 1];
for (auto [p, x, y] : nxt[i]) {
if (L >= l[p] && R <= r[p]) {
inc(f[i][j], 1ll * f[x][j] * f[y][j - 1] % kMod);
}
}
}
}
for (int i = 1; i <= cnt; ++i) f[i][0] = getval(f[i], seg[i].second - seg[i].first + 2, R - L + 1);
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> l[i] >> r[i];
prework();
std::fill_n(f[0], n + 4, 1);
for (int i = 1; i < m; ++i) solve(unq[i], unq[i + 1] - 1);
std::cout << f[1][0] << '\n';
}

int32_t main() {
freopen("robot.in", "r", stdin);
freopen("robot.out", "w", stdout);
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P5469 [NOI2019] 机器人 题解
https://sobaliuziao.github.io/2024/08/29/post/ba50cf9f.html
作者
Egg_laying_master
发布于
2024年8月29日
许可协议