不详细揭秘在线决策单调性 DP 的单 log 分治做法——2025.8.23 鲜花
不详细揭秘在线决策单调性 DP 的单 \(\log\) 分治做法
Cherry Pop
わーどきどき ねーすきすき?
哇~心跳加速 呐~喜欢喜欢?
あーズキズキ ちねちねちねちね
啊~刺痛刺痛 去死去死去死去死
あたし一等賞がほしいのよ
人家是要拿第一名的
二番なんて望んでない
才不稀罕当第二呢
みんなめんどくせって離れるの
大家都嫌麻烦 全都躲开我
重い子って不人気なん なんなん
“重女”就是没人气 搞什么嘛
あたし迷子 迷子で損な感じ
我总是迷路 感觉迷路怪吃亏的
本命になれないマン 死んじまうわ!
当不成你的本命 我要死掉啦!
サイコ? サイコはどっちどっち
地雷女?谁才是 谁才是地雷女?
おまえのことは顔しか 信じらんない!
你这个人 除了脸还有哪里可信!
やってないね やってらんないね
弄不下去了 真是受不了
一生ぼっち 好意ありがとさん
你就注孤生吧 好意心领啦
やってられるか~ったかたったった!
这谁受得了啊~啦 哒哒哒哒哒!
まじで
咱说实话
愛していい感 すきすき?
可以爱的感觉 喜欢喜欢?
恋していい感 すきすき?
可以喜欢的感觉 喜欢喜欢?
どれみが怖いぞ チェリーチェリー
Do Re Mi 好可怕哟 Cherry Cherry
そうでもない感 むりむり?
感觉 “也就那样吧”不行不行?
どうでもいい感 むりむり?
感觉 “无所谓啦” 不行不行?
トゲみが怖いぞ ベイビーベイビー
带刺儿的感觉好可怕哦 Baby Baby
愛していい感 すきすき?
可以爱的感觉 喜欢喜欢?
恋していい感 すきすき?
可以喜欢的感觉 喜欢喜欢?
都合が良くてよ チェリーチェリー
倒是挺会占便宜嘛 Cherry Cherry
そうでもない感 むりむり?
感觉 “也就那样吧”不行不行?
どうでもいい感 むりむり?
感觉 “无所谓啦” 不行不行?
出直してきなよ ベイビーベイビー
给我重新来过啦 Baby Baby
いやーほんと…
哎呀—真是的…
わーどきどき ねーすきすき?
哇~心跳加速 呐~喜欢喜欢?
あーズキズキ ちねちねちねちね
啊~刺痛刺痛 去死去死去死去死
あたし一等賞がほしいのよ
本公主要拿第一名哟
二番なんて望んでない
才不稀罕当第二呢
時に先生 好きとはなんですか
有时也会问问老师 “喜欢”到底是什么呀
辞書にないやつをください
给我一个字典里没有的答案吧
怒りぐっとこらえて言う「ごめんね」
强压怒火 说声「抱歉啦」
おまえのすきはすきじゃない 吐いちまうわ!
你的喜欢算什么喜欢 要吐了啦!
終わったおバカはどっかいって
没救了的笨蛋 快快消失吧!
あたしは王子様を待っているの
人家可是在等王子殿下的
やってないね やってらんないね
弄不下去了 真是受不了
殺菌よろしく ピュアに敬礼
快给我消毒 向纯洁致敬!
やってないね やってらんないね
我才没干过 真是受不了
一生ぼっち 好意ありがとさん
你就注孤生吧 好意心领啦
やってられるか~!
我真的受不了了~!
まじで…はぁ…ったかたったった!
真的啦…哈啊…咔哒哒哒哒!
まじで
真的啦
愛していい感 すきすき?
可以爱的感觉 喜欢喜欢?
恋していい感 すきすき?
可以喜欢的感觉 喜欢喜欢?
どれみが怖いぞ チェリーチェリー
Do Re Mi 好可怕哟 Cherry Cherry
そうでもない感 むりむり?
感觉 “也就那样吧”不行不行?
どうでもいい感 むりむり?
感觉 “无所谓啦” 不行不行?
トゲみが怖いぞ ベイビーベイビー
带刺儿的感觉好可怕哦 Baby Baby
愛していい感 すきすき?
可以爱的感觉 喜欢喜欢?
恋していい感 すきすき?
可以喜欢的感觉 喜欢喜欢?
都合が良くてよ チェリーチェリー
倒是正合心意 Cherry Cherry
そうでもない感 むりむり?
感觉 “也就那样吧”不行不行?
どうでもいい感 むりむり?
感觉 “无所谓啦” 不行不行?
出直してきなよ ベイビーベイビー
给我重新来过啦 Baby Baby
はい解散
好 解散!
わーどきどき ねーすきすき?
哇~心跳加速 呐~喜欢喜欢?
あーズキズキ ちねちねちねちね
啊~刺痛刺痛 去死吧去死吧去死吧去死吧
せーのっ
预备——起!
有说法叫 简易版 LARSCH 算法。
有抄袭参考 Register_int 的 在线决策单调性地皮还能单老哥分治做? - 洛谷专栏。
感觉有点将 Cdq 和离线分治混起来写的感觉。
有点牛。
考虑一个函数 \(S(l, r)\),我们希望调用这个函数获得 \(dp_l \sim dp_r\) 的值。
显然不分段算贡献是不现实的,所以我们分段算。
我们认为调用 \(S(l, r)\) 时 \(dp_1\sim dp_{l - 1}\) 都是处理好的,\(i \in [1, l)\) 对 \(dp_r\) 的贡献时已经处理好的,设 \(i\) 的决策点时 \(p_i\)(注意此时不区分是否完全转移好)。
我们:
- 枚举 \(j \in [p_{l - 1}, p_r]\) 更新 \(dp_{mid}\)。
- 递归 \(S(l, mid)\)。
- 用 \(j\in [l, mid]\) 更新 \(dp_{r}\)。
- 递归 \(S(mid + 1, r)\)。
复杂度和正确性都比较显然。
十分甚至九分的好写,更牛的是可以用离线分治的莫队,唯一要注意的是第一步和第三步的莫队分开开。
Code
/*
ulimit -s 128000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra && time ./%< && size %<
ulimit -s 128000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra -fsanitize=address,undefined -g && time ./%< && size %<
echo && cat out.out && echo
*/
#include <bits/stdc++.h>
using namespace std;
using llt = long long;
using llf = long double;
using ull = unsigned long long;
#define endl '\n'
#ifdef LOCAL
FILE *InFile = freopen("in.in", "r", stdin), *OutFile = freopen("out.out", "w", stdout);
#endif
const int N = 1e5 + 3;
llt dp[N], sp[N], si[N], _a; int n, _k, ps[N];
llt Wt(int l, int r){
if(!l) return r * sp[r] - si[r];
else if(r > n) return si[n] - si[l - 1] - l * (sp[n] - sp[l - 1]);
int mid = (l + r) >> 1;
return si[mid] - si[l - 1] - l * (sp[mid] - sp[l - 1]) + r * (sp[r] - sp[mid]) - si[r] + si[mid];
}
void Upd(int i, int j){ llt v = dp[i] + Wt(i, j) - _a; if(dp[j] > v) dp[j] = v, ps[j] = i; }
void Dp(int l, int r){
if(l == r) return ;
int mid = (l + r) >> 1;
for(int i = ps[l - 1]; i <= ps[r] && i < mid; ++i) Upd(i, mid);
Dp(l, mid);
for(int i = l; i <= mid; ++i) Upd(i, r);
Dp(mid + 1, r);
}
llt F(){
memset(dp + 1, 0x3f, sizeof(*dp) * n), Upd(0, n), Dp(1, n);
llt mi = 0x3f3f3f3f3f3f3f3f;
for(int i = 1; i <= n; ++i) mi = min(mi, dp[i] + Wt(i, n + 1));
return mi;
}
llt Wqs(){
auto G = [](llt a){ return _a = a, F() + a * _k; };
llt l = -1e12, r = 1e12;
while(l <= r){
llt mid = (l + r) >> 1, a = G(mid), b = G(mid + 1);
if(a == b) return a;
else if(a < b) l = mid + 1;
else r = mid - 1;
}
return G(l);
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> _k;
for(int i = 1; i <= n; ++i) cin >> sp[i], si[i] = sp[i] * i + si[i - 1], sp[i] += sp[i - 1];
cout << Wqs() << endl;
}
CF868F Yet Another Minimization Problem
这个是离线莫队维护贡献的板子,懒得写了。
P9266 【PA 2022】 Nawiasowe podziały
Code
/*
ulimit -s 128000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra && time ./%< && size %<
ulimit -s 128000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra -fsanitize=address,undefined -g && time ./%< && size %<
echo && cat out.out && echo
*/
#include <bits/stdc++.h>
using namespace std;
using llt = long long;
using llf = long double;
using ull = unsigned long long;
#define endl '\n'
#ifdef LOCAL
FILE *InFile = freopen("in.in", "r", stdin), *OutFile = freopen("out.out", "w", stdout);
#endif
const int N = 1e5 + 3;
int n, _k, _a[N], pl[N], pr[N], sl[N], sr[N];
class MO{
private:
int l = 1, r = 0; llt ans = 0;
int __tu[N << 1], *const tu = __tu + N;
public:
llt Qry(int ql, int qr){
++qr;
while(r < qr){
++r;
if(pl[r] < l) ans += tu[_a[r]];
else ans += sl[r];
++tu[_a[r]];
}
while(l > ql){
--l;
if(pr[l] > r) ans += tu[_a[l]];
else ans += sr[l];
++tu[_a[l]];
}
while(r > qr){
--tu[_a[r]];
if(pl[r] < l) ans -= tu[_a[r]];
else ans -= sl[r];
--r;
}
while(l < ql){
--tu[_a[l]];
if(pr[l] > r) ans -= tu[_a[l]];
else ans -= sr[l];
++l;
}
return ans;
}
} M1, M2;
namespace WQS{
int ps[N]; llt dp[N], _a = 0;
void Upd(int j, int i, auto &M){
llt v = dp[j] + M.Qry(j + 1, i) - _a;
if(dp[i] > v) dp[i] = v, ps[i] = j;
}
void Dp(int l, int r){
if(l == r) return ;
int mid = (l + r) >> 1;
for(int i = ps[l - 1]; i <= ps[r] && i < mid; ++i) Upd(i, mid, M1);
Dp(l, mid);
for(int i = l; i <= mid; ++i) Upd(i, r, M2);
Dp(mid + 1, r);
}
llt F(){
memset(dp + 1, 0x3f, sizeof(*dp) * n), Upd(0, n, M1);
return Dp(1, n), dp[n];
}
llt Wqs(){
auto G = [](llt a){ return _a = a, F() + a * _k; };
llt l = -1e10, r = 1e10;
while(l <= r){
llt mid = (l + r) >> 1, a = G(mid), b = G(mid + 1);
if(a == b) return a;
else if(a < b) l = mid + 1;
else r = mid - 1;
}
return G(l);
}
}
vector<pair<int, int>> qry[N];
int __tu[N << 1], *const tu = __tu + N;
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> _k; ++n;
for(int i = 2; i <= n; ++i){ char c; cin >> c, _a[i] = (c == '(' ? 1 : -1) + _a[i - 1]; }
stack<int> stk; _a[0] = -0x3f3f3f3f, _a[n + 1] = -0x3f3f3f3f, stk.emplace(0);
for(int i = 1; i <= n + 1; ++i){
while(_a[stk.top()] > _a[i]) pr[stk.top()] = i, stk.pop();
stk.emplace(i);
}
stk.pop(), stk.pop(), stk.emplace(n + 1);
for(int i = n; ~i; --i){
while(_a[stk.top()] > _a[i]) pl[stk.top()] = i, stk.pop();
stk.emplace(i);
}
for(int i = 1; i <= n; ++i) qry[max(1, pl[i]) - 1].emplace_back(i, -1), qry[i - 1].emplace_back(i, 1);
for(int i = 1; i <= n; ++i){
++tu[_a[i]];
for(auto [k, v] : qry[i]) sl[k] += tu[_a[k]] * v;
qry[i].clear();
}
memset(__tu, 0, sizeof __tu);
for(int i = 1; i <= n; ++i) qry[i].emplace_back(i, -1), qry[min(pr[i], n)].emplace_back(i, 1);
for(int i = 1; i <= n; ++i){
++tu[_a[i]];
for(auto [k, v] : qry[i]) sr[k] += tu[_a[k]] * v;
qry[i].clear();
}
--n;
cout << WQS::Wqs();
}
P
本文来自博客园,作者:xrlong,转载请注明原文链接:https://www.cnblogs.com/xrlong/p/19022132
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。