HTP10590 只为一人封刀 题解——2025.9.25 鲜花
HTP10590 只为一人封刀 题解
サワーチェリーが輝いたから + 弦楽少女は諦めを知らずに = 幕を下ろそう、パレードへ
本段落中所使用的歌词,其著作权属于原著作权人,仅以介绍为目的引用。
因为那颗酸樱桃闪闪发光 弦乐少女不知何谓放弃
以上是贺的萌娘百科
以下也是
感觉牛炸了,各种意义上
感觉是好题。
暴力网络流是显。
定义宝石是左部点集合 \(|A|\),剑是右部点集合 \(|B|\)。
首先典的考虑 Hall 定理:最大流是 \(\sum_{i = 1} ^ n a_i - \max_{S}\{|S| - |{\rm N}(S)|\}\),\(S \subseteq B\) 是右部点的集合,\({\rm N}(S)\) 是 \(S\) 所连的左部点。
我们考虑 \(type_i = 1\) 怎么做,先固定 \(x\),此时我们所有区间都包含 \(x\) 这个点,此时我们发现我们可以轻松钦定所有 \(L \le l_i \le x \le r_i \le R\) 的宝石 \(i\) 不选,考虑这时我们尽量多的选右部点,我们发现我们可以选的是 \(<l\) 和 \(>r\) 的前后缀。
简单推一下式子,我们要对所有 \(L \le x \le R\) 求:
去除容易计算的常量,我们要求的是
考虑分治,设当前是 Solve(l, r) : mid = (l + r) / 2
,我们只需要处理所有 \(l \le L \le mid < R \le r\) 的 \([L, R]\) 对 \(x\in [L, R]\) 的贡献。
我们直接扫描 \(L\) 并用线段树维护 \(\max R\) 并另外维护 \(x \in [L, mid]\) 的 checkmax 即可,对 \(R\) 同理。
考虑怎么做 \(type_i = 0\)。
首先对于所有 \(L \le l_i \le r_i \le R\) 的区间显然是选不上的,我们直接和 \(type_i = 1\) 的一起处理。
我们先钦定其他所有的 \(type_i = 0\) 的宝石都选上,现在我们考虑选上一些区间,这样我们就要减掉一些 \(b_i\) 并减掉其对应的一些 \(a_i\) 的贡献。
我们发现对于 \(l_i \le L - 1 \le r_i \le R\) 的贡献我们会在 \(L' = l_i\) 的 \([L', R]\) 中统计到,这样我们其实最后只需要考虑 \(r - 1 < L\) 和 \(R + 1 < l\) 的部分,同时因为和 \([L, R]\) 之间至少隔着一个点所以也不会对 \(type_i = 1\) 的点产生贡献。
于是问题就变成了一个前后缀问题,我们直接 dp,设 \(f_i\) 表示 \(i\) 前缀的贡献,\(g_i\) 表示 \(i\) 后缀的贡献。我们考虑 \(f_i\) 的转移方程:
道理就是枚举一个后缀的 \(a\) 全部扔掉,\(g\) 同理。
这个 dp 显然直接扫描线加线段树优化一下直接就做完了,最后我们 checkmax 的东西就是
注意最后我们要对每个 \(x\) checkmax \(f_x + g_{x + 1}\),别忘了用 \(\sum b\) 减。
\(n, m\) 同阶,复杂度 \({\cal O}(n \log^2 n)\)。
能不能再给力一点啊!
发现复杂度上界是 \(type_i = 1\) 的分治上线段树,这能忍住不优化到线段树分治?
我们考虑分治的过程中我们干了什么,考虑 Solve(l, r) : mid = (l + r) / 2
的扫描 \(L\) 的过程,\(R\) 同理。
首先 checkmax 可以直接一个前缀 \(\max\) 状物 \({\cal O}(mid - l + 1)\) 做。
我们发现首先我们需要将所有 \(mid < l_i \le r_i \le r\) 的 \(b_i\) 加上,这个操作我们在 Solve(mid + 1, r)
时干了。
然后我们对于所有 \(l_i = L, r_i \le r\) 的区间,我们要贡献 \(b_i\)。考虑贡献,我们事实上是 \([\max\{b_i, mid + 1\}, r]\) 的区间加。
并且还有 \(mid - l + 1\) 次 \([mid + 1, r]\) 求 \(\max\)。
我们直接在线段树上跑,这样 \([mid + 1, r]\) 在线段树上是一个节点,单次查询是 \({\cal O}(1)\) 的,对于修改,我们发现若 \(b_i <= mid\),\([mid + 1, r]\) 的区间加依然是 \({\cal O}(1)\) 的,而对于所有区间,其剩下的贡献都只有一次,即只会在 \(l \le l_i \le mid < r_i \le r\) 的 Solve(l, r)
中贡献,总次数是 \({\cal O}(m)\) 的,这部分的复杂度依然是 \({\cal O}(m\log n)\) 的。
我们认为 \(n, m\) 同阶,复杂度就是 \({\cal O}(n \log n)\) 的了。
我代码中 df[i]
是 \(-f_i\),dg
是 \(-g_i\)。
Code
/*
ulimit -s 1024000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra && ulimit -v 128000 && ./%<
ulimit -s 1024000 && clear && g++ % -o %< -O2 -std=c++14 -DLOCAL -Wall -Wextra -fsanitize=address,undefined -g && ./%<
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 = 2e5 + 3;
const llt Inf = 0x3f3f3f3f3f3f3f3fll;
int n, m, _a[N]; llt qa[N], aas[N], df[N], dg[N];
vector<pair<int, int>> pla[N], pra[N], plb[N], prb[N];
#define lson (t << 1)
#define rson (t << 1 | 1)
#define mid ((l + r) >> 1)
class Seg{
private:
static const int D = N << 2;
llt nd[D], tg[D]; function<llt(llt, llt)> F;
void add(llt v, int t){ nd[t] += v, tg[t] += v; }
void Dwn(int t){ if(llt &x = tg[t]) add(x, lson), add(x, rson), x = 0; }
void Upd(int t){ nd[t] = F(nd[lson], nd[rson]); }
public:
Seg(const auto &f) : F(f){}
void Add(int L, int R, llt v, int l = 1, int r = n, int t = 1){
if(l == L && r == R) return add(v, t), void();
Dwn(t);
if(R <= mid) Add(L, R, v, l, mid, lson);
else if(L > mid) Add(L, R, v, mid + 1, r, rson);
else Add(L, mid, v, l, mid, lson), Add(mid + 1, R, v, mid + 1, r, rson);
Upd(t);
}
llt Get(){ return nd[1]; }
void Clr(){ memset(nd, 0, sizeof nd), memset(tg, 0, sizeof tg); }
friend void Cdq(int, int, int);
} pre([](llt a, llt b){ return a < b ? a : b; }),
nxt([](llt a, llt b){ return a > b ? a : b; }),
seg([](llt a, llt b){ return a < b ? a : b; });
llt tp[N];
void Cdq(int l = 1, int r = n, int t = 1){
if(l == r){
llt s = 0; pre.nd[t] = -qa[l - 1] + df[l - 1], nxt.nd[t] = -qa[l] - dg[r + 1];
for(auto [nr, v] : plb[l]) if(nr == r) pre.add(-v, t), nxt.add(v, t), s += v;
return aas[l] = max(aas[l], s - qa[l] + qa[l - 1] - df[l - 1] - dg[r + 1]), void();
}
Cdq(l, mid, lson), Cdq(mid + 1, r, rson);
tp[l - 1] = tp[r + 1] = 0;
for(int nl = mid; nl >= l; --nl){
for(auto [nr, v] : plb[nl]) if(nr <= r) nxt.Add(max(nr, mid + 1), r, v, mid + 1, r, rson);
tp[nl] = nxt.nd[rson] + qa[nl - 1] - df[nl - 1];
}
for(int nl = l; nl <= mid; ++nl) aas[nl] = max(aas[nl], tp[nl] = max(tp[nl], tp[nl - 1]));
for(int nr = mid + 1; nr <= r; ++nr){
for(auto [nl, v] : prb[nr]) if(l <= nl) pre.Add(l, min(mid, nl), -v, l, mid, lson);
tp[nr] = -qa[nr] - pre.nd[lson] - dg[nr + 1];
}
for(int nr = r; nr > mid; --nr) aas[nr] = max(aas[nr], tp[nr] = max(tp[nr], tp[nr + 1]));
pre.Upd(t), nxt.Upd(t);
}
#undef lson
#undef rson
#undef mid
void Dp(){
seg.Add(1, n, Inf);
for(int r = 1; r <= n; ++r){
seg.Add(r, r, -Inf + df[r - 1]), seg.Add(1, r, _a[r]);
for(auto [l, v] : pra[r]) seg.Add(1, l, -v);
df[r] = min(df[r - 1], seg.Get());
}
seg.Clr(), seg.Add(1, n, Inf);
for(int l = n; l; --l){
seg.Add(l, l, -Inf + dg[l + 1]), seg.Add(l, n, _a[l]);
for(auto [r, v] : pla[l]) seg.Add(r, n, -v);
dg[l] = min(dg[l + 1], seg.Get());
}
}
void Solve(){
cin >> n >> m;
llt sb = 0;
for(int i = 1; i <= n; ++i) cin >> _a[i], qa[i] = qa[i - 1] + _a[i];
for(int i = 1, l, r, b, t; i <= m; ++i){
cin >> l >> r >> b >> t;
if(!t) pla[l].emplace_back(r, b), pra[r].emplace_back(l, b);
plb[l].emplace_back(r, b), prb[r].emplace_back(l, b), sb += b;
}
Dp(), Cdq();
for(int i = 1; i <= n; ++i) cout << sb - max(aas[i], -df[i] - dg[i + 1]) << ' ';
cout << endl;
}
void Clear(){
for(int i = 1; i <= n; ++i) aas[i] = 0, pla[i].clear(), pra[i].clear(), plb[i].clear(), prb[i].clear();
memset(df, 0, sizeof df), memset(dg, 0, sizeof dg);
pre.Clr(), nxt.Clr(), seg.Clr();
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int tid, t;
cin >> tid >> t;
while(t--) Solve(), Clear();
}
P
本文来自博客园,作者:xrlong,转载请注明原文链接:https://www.cnblogs.com/xrlong/p/19110909
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。