题解:P3336 [ZJOI2013] 话旧
zhang_kevin · · 题解
是 dp 好题!
题意简述
函数
关键性质
我们先观察一些性质。
-
函数值总是非负,因为极小值为
0 。 -
任何局部极小值(本题中,等价于下降后上升的转折点)必须恰好为
0 。 -
函数在整数点处的值均为整数,且每一步变化
1 或-1 。
这些性质非常重要。
动态规划状态
下面考虑设计 dp 状态。
我们关心处理了哪些已知点,以及上一步的方向。因此,考虑令
由于第一步只能向上走,因此有初始值
转移
对于两个相邻的已知点
显然我们需要操作的次数
记
下面我们分讨
无 0 路径
首先考虑没有
假设上升了
这个过程中路径唯一,最大值
有 0 路径
这类路径可以由两个整数
举个例子,我们可以这样描述一条路径:
-
第一段:从
y_1 开始先上升a 步,再下降a + y_1 步,到达0 。 -
中间段:许多从
0 \to 0 的山峰,高度为h_i 。 -
末尾段:从
0 上升b + y_2 步到达b + y_2 ,再下降b 步到达y_2 。
问题等价地转化为对合法的有序三元组
首先我们知道合法的路径总数为
不妨写出其生成函数:
(\sum_{a \geq 0} x^{a})(\sum_{b \geq 0} x^{b})(\sum_{k \geq 0}(\sum_{h \geq 1} x^{h})^{k})=\frac{1}{1-x} \cdot \frac{1}{1-x} \cdot \frac{1}{1-\frac{x}{1-x}}=\frac{1}{(1-x)(1-2x)} 展开得
\frac{1}{(1 - x)(1 - 2x)} = \sum\limits_{S \geq 0} (2^{S + 1} - 1)x^S ,故x^S 的系数为2^{S + 1} - 1 。证毕。
接下来分讨各种方向的可能情况,如下表所示:
| 第一步 | 最后一步 | 要求的条件 | 方案数 | 此时该段内最大值的最大值 |
|---|---|---|---|---|
| 上升 | 上升 | |||
| 上升 | 下降 | |||
| 下降 | 上升 | |||
| 下降 | 下降 |
注意,当
答案
下面分析最终答案,设加上
由于最后必须出现在
特别的,如果
时间复杂度
考虑分析时间复杂度。
前面排序的时间复杂度为
故总时间复杂度为
参考实现
下面给出这道题的 C++ 参考代码。
::::info[Code]{open}
提交记录:https://qoj.ac/submission/2085823。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline char gc(){return getchar();}
inline void pc(char ch){putchar(ch);}
inline int rd(){
int x = 0, f = 1;
char ch = gc();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = gc();
}
while(isdigit(ch)){
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = gc();
}
return x * f;
}
inline void wr(int x){
if(x < 0) pc('-'), x = -x;
if(x > 9) wr(x / 10);
pc(x % 10 + '0');
return;
}
const int Mod = 19940417;
inline int fp(int a, int b){
int res = 1;
while(b){
if(b & 1) res = res * a % Mod;
a = a * a % Mod;
b >>= 1;
}
return res;
}
#define y1 zhang_kevin
int n, k, f[1000005][2], g[1000005][2];
pair<int, int> fx[1000005];
#define fi first
#define se second
signed main(){
n = rd(), k = rd();
for(int i = 1; i <= k; i++) fx[i].fi = rd(), fx[i].se = rd();
fx[++k] = {0, 0}, fx[++k] = {n, 0};
sort(fx + 1, fx + 1 + k);
k = unique(fx + 1, fx + 1 + k) - fx - 1;
// for(int i = 1; i <= k; i++) cout << fx[i].fi << ' ' << fx[i].se << '\n';
memset(g, 0x80, sizeof(g));
f[1][0] = 1, g[1][0] = 0;
for(int i = 2; i <= k; i++){
int x1 = fx[i - 1].fi, y1 = fx[i - 1].se, x2 = fx[i].fi, y2 = fx[i].se;
// cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << '\n';
int d = x2 - x1, deltay = y2 - y1;
if(d >= abs(deltay) && d % 2 == abs(deltay) % 2){
int S = (d - y1 - y2) / 2;
{ // 无 0 路径
int u = (d + deltay) / 2, v = (d - deltay) / 2, mx = y1 + u;
if(u > 0){
if(v > 0){
if(y1 > 0) f[i][0] = (f[i][0] + f[i - 1][1]) % Mod;
if(y1 == 0) f[i][0] = (f[i][0] + f[i - 1][0]) % Mod;
if(y1 > 0) if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][1], mx});
if(y1 == 0) if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][0], mx});
}else{
if(y1 > 0) f[i][1] = (f[i][1] + f[i - 1][1]) % Mod;
if(y1 == 0) f[i][1] = (f[i][1] + f[i - 1][0]) % Mod;
if(y1 > 0) if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][1], mx});
if(y1 == 0) if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][0], mx});
}
}else{
if(v > 0){
f[i][0] = (f[i][0] + f[i - 1][1]) % Mod;
f[i][0] = (f[i][0] + f[i - 1][0]) % Mod;
if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][1], mx});
if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][0], mx});
}else{
f[i][1] = (f[i][1] + f[i - 1][1]) % Mod;
f[i][1] = (f[i][1] + f[i - 1][0]) % Mod;
if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][1], mx});
if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][0], mx});
}
}
}
if(S >= 0){ // 有 0 路径
if(S >= 1 && y2 > 0){
int cnt = fp(2, S - 1), mx = max(y1 + S, y2);
if(y1 > 0) f[i][1] = (f[i][1] + f[i - 1][1] * cnt % Mod) % Mod;
if(y1 == 0) f[i][1] = (f[i][1] + f[i - 1][0] * cnt % Mod) % Mod;
if(y1 > 0) if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][1], mx});
if(y1 == 0) if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][0], mx});
}
if(S >= 2){
int cnt = fp(2, S - 1) - 1, mx = max(y1 + S - 1, y2 + S - 1);
if(y1 > 0) f[i][0] = (f[i][0] + f[i - 1][1] * cnt % Mod) % Mod;
if(y1 == 0) f[i][0] = (f[i][0] + f[i - 1][0] * cnt % Mod) % Mod;
if(y1 > 0) if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][1], mx});
if(y1 == 0) if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][0], mx});
}
if(S >= 0 && y1 > 0 && y2 > 0){
int cnt = S == 0 ? 1 : fp(2, S - 1), mx = max(S, y2);
f[i][1] = (f[i][1] + f[i - 1][1] * cnt % Mod) % Mod;
f[i][1] = (f[i][1] + f[i - 1][0] * cnt % Mod) % Mod;
if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][1], mx});
if(f[i][1]) g[i][1] = max({g[i][1], g[i - 1][0], mx});
}
if(S >= 1 && y1 > 0){
int cnt = fp(2, S - 1), mx = y2 + S;
f[i][0] = (f[i][0] + f[i - 1][1] * cnt % Mod) % Mod;
f[i][0] = (f[i][0] + f[i - 1][0] * cnt % Mod) % Mod;
if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][1], mx});
if(f[i][0]) g[i][0] = max({g[i][0], g[i - 1][0], mx});
}
}
}else cout << "No Solution" << '\n';
}
wr(f[k][0]), pc(' '), wr(g[k][0]), pc('\n');
return 0;
}
::::