【题解】CatOJ C0458C baka's trick
标题 trick 的名字我也不知道是什么,就这样吧。
upd on 2024.7.18:该题 trick 名字为 baka's trick。
首先有显然的 dp 式子:
有显然的
Code(这份代码 5e5 的包 WA 了,但大致思路可以参考一下)
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb emplace_back
#define rep(_, __, ___) for (int _ = (__); _ <= (___); _++)
#define per(_, __, ___) for (int _ = (__); _ >= (___); _--)
// #define int long long
using ll = long long; using pii = pair<int, int>;
inline int read() {
char ch = getchar(); int s = 0, f = 1;
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * f;
}
constexpr int N = 5e5 + 5, mod = 1e9 + 7;
int n, k, a[N], s1[N], t1, t2; ll pw[N]; tuple<int, int, int> s2[N];
#define ls id << 1
#define rs id << 1 | 1
ll mn[N << 2], tag[N << 2];
inline void maketag(int id, int x) {
tag[id] += x, mn[id] += x;
}
void upd(int a, int b, int x, int id = 1, int l = 1, int r = n) {
if (a <= l && b >= r) return maketag(id, x);
int mid = (l + r) >> 1; maketag(ls, tag[id]), maketag(rs, tag[id]), tag[id] = 0;
if (a <= mid) upd(a, b, x, ls, l, mid);
if (b > mid) upd(a, b, x, rs, mid + 1, r);
mn[id] = min(mn[ls], mn[rs]);
}
ll qry(int a, int b, int id = 1, int l = 1, int r = n) {
if (a <= l && b >= r) return mn[id];
int mid = (l + r) >> 1; ll res = 1e18; maketag(ls, tag[id]), maketag(rs, tag[id]), tag[id] = 0;
if (a <= mid) res = min(res, qry(a, b, ls, l, mid));
if (b > mid) res = min(res, qry(a, b, rs, mid + 1, r));
return res;
}
signed main() {
// freopen("knight.in", "r", stdin);
// freopen("knight.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read(), k = read();
pw[0] = 1; rep (i, 1, n) a[i] = read(), pw[i] = pw[i - 1] * 23 % mod;
ll ans = 0, dp;
rep (i, 1, n) {
while (t1 && a[s1[t1]] < a[i]) t1--;
int x = s1[t1] + 1; s2[t2 + 1] = {get<1>(s2[t2]) + 1, i, 0}, t2++;
while (t2 && get<0>(s2[t2]) >= x) {
upd(get<0>(s2[t2]), get<1>(s2[t2]), a[i] - get<2>(s2[t2]));
t2--;
}
s2[++t2] = {x, i, a[i]}, s1[++t1] = i; dp = qry(max(i - k + 1, 1), i);
if (i < n) upd(i + 1, i + 1, dp);
ans += pw[n - i] * (dp % mod) % mod;
}
cout << ans % mod;
return 0;
}
/*
3 4 4 8 10 12
*/
进阶一下,上面的线段树优化 dp 可以每次修改的都是一个后缀,可以用可删堆维护
于是我们得到了乱搞做法:把上面的可删堆换成压位 Trie,时间复杂度是
观察复杂度瓶颈在于可删堆的求
分析一下复杂度:如果右端点不扩,势能是不断减小的,即
Code
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb emplace_back
#define rep(_, __, ___) for (int _ = (__); _ <= (___); _++)
#define per(_, __, ___) for (int _ = (__); _ >= (___); _--)
#define int long long
using ll = long long; using pii = pair<int, int>;
inline int read() {
char ch = getchar(); int s = 0, f = 1;
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * f;
}
constexpr int N = 1e7 + 5, mod = 1e9 + 7;
int n, k, st = 1, ed, mid = 1, dp[N], mn[N], pw[N];
struct node {signed l, r; int tag;} s[N];
inline int get(int i) {
return s[i].tag + dp[s[i].l - 1];
}
inline void rebuild() {
if (st > ed) return (void)(mid = st); mid = (st + ed) >> 1;
rep (i, st, ed) mn[i] = get(i);
per (i, mid - 1, st) mn[i] = min(mn[i], mn[i + 1]);
rep (i, mid + 2, ed) mn[i] = min(mn[i], mn[i - 1]);
}
signed main() {
// freopen("knight.in", "r", stdin);
// freopen("knight.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = read(), k = read(); int ans = 0;
pw[0] = 1; rep (i, 1, n) pw[i] = pw[i - 1] * 23 % mod;
rep (i, 1, n) {
int x = read();
// cerr << s[st].l << '*' << s[st].r << endl;
while (st <= ed && s[st].l < i - k + 1) {
if (s[st].r < i - k + 1) {
if (++st > mid) rebuild();
}
else {
s[st].l = i - k + 1; mn[st] = get(st);
if (st < mid) mn[st] = min(mn[st], mn[st + 1]);
}
}
// cerr << s[st].l << '$' << s[st].r << endl;
while (st <= ed && s[ed].tag <= x) if (--ed <= mid) rebuild();
// cerr << s[st].l << '&' << s[st].r << endl;
if (st > ed) s[++ed] = (node){max(1ll, i - k + 1), i, x};
else s[ed + 1] = (node){s[ed].r + 1, i, x}, ed++;
mn[ed] = get(ed); if (ed - 1 > mid) mn[ed] = min(mn[ed], mn[ed - 1]);
dp[i] = min(mn[ed], mn[st]); ans += pw[n - i] * (dp[i] % mod) % mod;
// cerr << i << ' ' << dp[i] << '\n';
}
cout << ans % mod;
return 0;
}
/*
3 4 4 8 10 12
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)