【CF802O】April Fools' Problem (hard) 题解 (线段树模拟费用流)
线段树模拟费用流。
Solution
Part 1
根据题面,显然想到此题是费用流。建图方式亦是显然:
,流量为 ,费用为 ; ,流量为 ,费用为 ; ,流量为 ,费用为 ; ,流量为 ,费用为 。
观察数据,知道直接跑费用流肯定会起飞。所以我们选择模拟费用流。
Part 2
不难抓住题目条件的特性:第
具体地,记“第一次处理”为
进而,我们现在的目标转化为:在括号序列合法的前提下,每次放入一左一右两个括号,重复
Part 3
假设将左括号放在第 ..(..)..
与 ..)..(..
。
最直接的方式就是线段树动态维护两个序列的最小值,每次取最小值即可。这种方式对第一种情况是适用的。
但是因为括号序列前缀数组的特殊限制,对于第二种情况,需要满足前提条件:对于
而我们使用线段树动态维护的,是对于区间
如何维护第二种情况?如果直接维护权值是否为
故,对于某一区间
与 :满足下标在 范围内, 与 分别是各自序列中的最小值; 与 :仅针对情况二,维护当前区间满足情况二限制的前缀 与 ,辅助情况二求解; :下标在 内, 序列中的项的最小值; :当前区间对于情况一的答案(一数对); :当前区间对于情况二的答案(一数对,且考虑情况二的特殊限制); :当前区间对于情况二的临时答案(一数对,且不考虑情况二的特殊限制)。
然后区间合并时,以上变量之间的合并差不多是基本的常见操作,
Part 4
时复
另:此做法可求出
Code
内附有注释。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define ls (x<<1)
#define rs (x<<1|1)
const int maxn = 5e5 + 5, inf = 0x3f3f3f3f;
int n, k, a[maxn], b[maxn];
ll ans;
struct node{int x, y;};
struct tree{
int ma, mb, la, lb, mn, tg;
node va, vb, vc;
}t[maxn << 2];
inline bool operator <(node x, node y){
return a[x.x] + b[x.y] < a[y.x] + b[y.y];
}
inline tree operator +(tree x, tree y){
tree z; z.tg = 0;
if(a[x.ma] < a[y.ma]) z.ma = x.ma; else z.ma = y.ma;
if(b[x.mb] < b[y.mb]) z.mb = x.mb; else z.mb = y.mb;
z.mn = min(x.mn, y.mn);//以上三者直接取最小
z.va = min((node){x.ma, y.mb}, min(x.va, y.va));
z.vc = min((node){y.ma, x.mb}, min(x.vc, y.vc));//在没有特殊限制时,两区间合并可产生新的、符合条件的数对
z.vb = min(x.vb, y.vb);
if(x.mn > y.mn){
//此时,x所代表的区间内的所有数 都严格大于合并后区间最小值,所以x区间内的数可直接取最小
z.vb = min(z.vb, min((node){y.la, x.mb}, x.vc));
z.la = (a[x.ma] < a[y.la] ? x.ma : y.la), z.lb = y.lb;
/*z.la=y.la等价于这个前缀直接涵盖了x区间,并与y区间的la前缀接上了*/
} else if(y.mn > x.mn){
z.vb = min(z.vb, min((node){y.ma, x.lb}, y.vc));
z.la = x.la, z.lb = (b[y.mb] < b[x.lb] ? y.mb : x.lb);
} else{ z.la = x.la, z.lb = y.lb;
z.vb = min(z.vb, (node){y.la, x.lb});//直接合并前后缀
} return z;
}
inline void psd(int x){
if(!t[x].tg) return;
t[ls].tg += t[x].tg, t[ls].mn += t[x].tg;
t[rs].tg += t[x].tg, t[rs].mn += t[x].tg;
t[x].tg = 0;
}
inline void psp(int x){ t[x] = t[ls] + t[rs];}
inline void build(int x, int l, int r){
if(l == r) return
t[x] = (tree){l, l, l, 0, 0, 0, (node){l, l}, (node){0, 0}, (node){l, l}}, void();
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r), psp(x);
}
inline void updt1(int x, int l, int r, int p){
if(l == r) return;
int mid = l + r >> 1; psd(x);
if(p <= mid) updt1(ls, l, mid, p); else updt1(rs, mid + 1, r, p);
psp(x);
}
inline void updt2(int x, int l, int r, int L, int R, int p){
if(l > R or L > r) return;
if(L <= l and r <= R) {t[x].tg += p, t[x].mn += p; return;}
int mid = (l + r) >> 1; psd(x);
updt2(ls, l, mid, L, R, p), updt2(rs, mid + 1, r, L, R, p);
psp(x);
}
int main(){
scanf("%d%d", &n, &k);
rep(i, 1, n) scanf("%d", &a[i]); rep(i, 1, n) scanf("%d", &b[i]);
a[0] = b[0] = inf; build(1, 0, n);
while(k--){ int x, y, p;
if(t[1].va < t[1].vb) x = t[1].va.x, y = t[1].va.y, p = 1;
else x = t[1].vb.x, y = t[1].vb.y, p = -1;
ans += a[x] + b[y]; a[x] = b[y] = inf;
updt1(1, 0, n, x), updt1(1, 0, n, y);
updt2(1, 0, n, min(x, y), max(x, y) - 1, p);
} printf("%lld\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?