题解:AT_abc457_f [ABC457F] Second Gap / ARIS0_0 - 37
前言
我他妈为什么没有 AK 这场简单的比赛。
思路
我们天真的考虑直接把最大值和最小值直接塞到 dp 状态里面,然后我们的 dp 雏形是这样的:
不过这确实很天真,毕竟你的最大和次大是从后面来的,你这个
不难发现,
先不谈怎么转移,你发现这个状态数量有点多,考虑通过这个转移方式去减少一些状态。发现,出现第三种情况当且仅当
于是,你发现,
这样子初始化就是直接
第一步,如果
否则如果
第二部,将
这一步是显然的,就是如果你替换了最大值或是次大值,那么最大值是原本的最大值位置或是你本身,不难发现,如果要用你替换最大和次大之一,原本的最大值一定在
最后的答案就是所有的
然后,你考虑上面这个“单点加,区间乘,区间或单点查询和”,好像就是一个线段树板子。
然后注意取模你就做完了。
code
语言可能比较混乱,所以代码罕见的加了注释!
:::success[code]
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define piii pair<pii, int>
#define fi first
#define se second
const int N = 2e5 + 5, M = 1e6 + 5;
const int inf = 1e18, mod = 998244353; // 注意模数
namespace ARIS0_0{
int n, d[N];
struct segment{
int tr[N << 2], mul[N << 2]; // 四倍空间,以及乘法 lazy tag
void addmul(int now, int x){
tr[now] = 1ll * tr[now] * x % mod, mul[now] = 1ll * mul[now] * x % mod;
// 下放 lazy tag,记得取模
}
void pushup(int now){
tr[now] = (tr[now << 1] + tr[now << 1 | 1]) % mod;
// 维护区间和
}
void pushdown(int now){
if (mul[now] == 1) return ;
addmul(now << 1, mul[now]);
addmul(now << 1 | 1, mul[now]);
mul[now] = 1;
}
void modify_add(int now, int l, int r, int pos, int x){ // 单点加
if (l == r){
tr[now] = (tr[now] + x) % mod; // 取模
return ;
}
pushdown(now); // 下放 lazy tag
int mid = (l + r) >> 1;
if (pos <= mid) modify_add(now << 1, l, mid, pos, x); // to left
else modify_add(now << 1 | 1, mid + 1, r, pos, x); // to right
pushup(now); // 下方 lazy tag
}
int query(int now, int l, int r, int pos){ // 基础的线段树查询
if (l == r) return tr[now];
pushdown(now);
int mid = (l + r) >> 1;
if (pos <= mid) return query(now << 1, l, mid, pos); // to left
return query(now << 1 | 1, mid + 1, r, pos); // to right
}
} seg;
void init(){
}
void solve(){
cin >> n;
for (int i = 1; i < n; i ++ ) cin >> d[i]; // 读入
for (int i = 1; i <= n + n + n + n; i ++ ) seg.mul[i] = 1, seg.tr[i] = 0; // 记得初始化
seg.modify_add(1, 1, n, n, 1), seg.modify_add(1, 1, n, n - 1, 1); // dp[n - 1][n] = dp[n - 1][n - 1] = 1 对应的线段树操作。
for (int i = n - 2; i >= 1; i -- ){
// d[i] = d[i + 1] -> dp[i][any] = dp[i + 1][any] * (n - i - 1)
// dp[i][i], dp[i][i + d[i]] add dp[i + 1][i + d[i]]
int tmp = seg.query(1, 1, n, i + d[i]); // 注意要在下面的操作之前查询
if (d[i] == d[i + 1]) seg.addmul(1, n - i - 1); // d[i] = d[i + 1] 的特殊情况
else seg.addmul(1, 0); // 技巧:清空就是乘 0
seg.modify_add(1, 1, n, i, tmp), seg.modify_add(1, 1, n, i + d[i], tmp); // i覆盖了最大值或者次大值
}
cout << seg.tr[1] << "\n"; // 技巧:全部和,直接拿 1
}
void single(){ init(), solve(); }
void multi(){ init(); int T; cin >> T; while (T -- ) solve(); }
void idmulti(){ init(); int id, T; cin >> id >> T; while (T -- ) solve(); }
};
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
ARIS0_0::single();
}
// 你听啊冬至的白雪,你听它掩饰着哽咽,在没有你的世界,再没有你的冬眠
// ARIS0_0 - 37
:::