HTP10590 只为一人封刀 题解——2025.9.25 鲜花

HTP10590 只为一人封刀 题解

サワーチェリーが輝いたから + 弦楽少女は諦めを知らずに = 幕を下ろそう、パレードへ

本段落中所使用的歌词,其著作权属于原著作权人,仅以介绍为目的引用。

 因为那颗酸樱桃闪闪发光 弦乐少女不知何谓放弃

チョコレートバター
        リズムと歌詞合わせて
チョコレート&バター 溶かし合わせて
卵に砂糖菓子
  メロディちょこっと混ぜる
卵に砂糖を ちょこっと混ぜる
小麦粉 (できれば薄力粉)
  パンフルート鳴り
小麦粉 振るう隣で
メレンゲの雪景色、の森で
デクレッシェンド 眩ませて
メレンゲ、    らませて
天狼   の壊乱を  抜け
   を巻く食べかけのキャンディ
ペンローズの階段 駆け抜け
インス  ンスリダイヤルして
   リンドヴルム 唸るような
インスリン足  りなくなる様な
ハート捕 まえし出せば
   路上にて焦がしている
ハートの錠前 してる
   脳内Lorelei!
嗅ぎつけたら動くから
鍵  の無い獄から
日は静 まりった、時も 暮れて
   苦しく   交わす言葉たち
冷やしまった時も  忘 れて
あなた  後ろ追いかける
   だけレシピ口遊み
穴だ らけレシピ口ずさみ
幾つ  く気が付いて
  モノ  象っても
幾つものウス辿っても
  カラカラ廻る
いつもの一  歩の先へ
いつかダ・カーポの先へ
捲る  マーブルの絵日記に
  る時    諦めを知らずに
眩 暈くマーブルの煌めきに
思いよ  け 
   解くWaldfort
思いっきりくAlfortと
グリニッジ子午線  真冬の空へと
        負のオーラ プレゼント
      五線譜のマフラープレゼント
  続く大盛況!
いて座のショー•ウィンドウへ
凍てつく大聖     堂へ
甘さ  は真っ青なヘド ニア
  を砕く   気は向かないけど
甘さには甘さを、泣き止むには
 まったりで
 立ち塞がる 摩訶論も
たち二人で マカロンも
サイケデリック 患いを拗らせたなら
         飛び越えてあげるのさ
      食わずをこじ 開けるのさ
  死の行進を!
おかわり大音量で
お菓子の香辛料で
シナモンとシュガー
       歪むレシオと薔薇を
シナモンとシュガー、塩とバターを
強めの仄暗さに
    心に詰めていけば
強めの炎で煮詰めていけば
 卵と (少しだけ勇気を)
飽きの来ないletterと
 卵と粉入れたあと
絞ればそこは甘美なパラダイス
words...  出来上がり!
絞れば――出来上がり!
空に  ワッフルが   降っている
  け!甘い     トルテのヴィオラで
空にヒビWhat follows the tall tale?
ここでもう素な悪夢も
   また虫に慣れそうな
ここでまた無に成れそうな
終わり  魔法でサワーチェリーが
   ナイトメア退かしていく
終わりない魔法でとかしていく
 輝いたから
もいなくておかしいよ
かが焼いたお菓子を
者、リズム傷つ くと
 苦悩は寒 々、絶え間も無く
リズムむタクト
kühler und nüchtern 楽譜を綴れば 
     リノリウム  を潔むヴィオラ
黒塗   りの   楽譜詠むヴィオラ
HAST DU KÄSE? ロンリー·ガールのストーリーを
      のめり込み    繫がって
歯抜け   のメ ロディが  繋がって
  戻そう、  パレットへ!
マクデブルクの パレートへ!
幕 を下ろそう パレードへ!

以上是贺的萌娘百科

以下也是

因为那颗酸樱桃闪闪发光

弦乐少女不知何谓放弃

幕を下ろそう、パレードへ

感觉牛炸了,各种意义上

感觉是好题。

暴力网络流是显。

定义宝石是左部点集合 \(|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\) 求:

\[\sum_{i = 1} ^ n a_i - \max\{\sum_{i = 1} ^ n a_i - \sum_{i = l}^r a_i - \sum_{i = 1}^m b_i + \sum_{i = 1}^m [L \le l_i \le r_i \le R]b_i\} \]

去除容易计算的常量,我们要求的是

\[\max\{\sum_{i = 1}^m [L \le l_i \le r_i \le R]b_i - \sum_{i = l}^r a_i\} \]

考虑分治,设当前是 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\) 的转移方程:

\[f_i = \max\{\max_{j = 1}^{i}\{f_{j - 1} + \sum_{k = 1}^m [j \le l_k \le r_k \le i]b_k - \sum_{k = j}^ia_k\}, f_{i - 1}\} \]

道理就是枚举一个后缀的 \(a\) 全部扔掉,\(g\) 同理。

这个 dp 显然直接扫描线加线段树优化一下直接就做完了,最后我们 checkmax 的东西就是

\[\sum_{i = 1}^m [L \le l_i \le r_i \le R]b_i - \sum_{i = l}^r a_i + f_{L - 1} + g_{R + 1} \]

注意最后我们要对每个 \(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





posted @ 2025-09-25 14:32  xrlong  阅读(83)  评论(0)    收藏  举报

Loading