题解:P11587 [KTSC 2022 R2] 编程测试
本文默认下标从
考虑这个复杂度很大的算法:先二分答案,检查函数中建网络流判断是否流满。
这个算法显然是正确的,并且它启示我们这个题相当于匹配问题,我们尝试用 Hall 定理分析出充要条件:
- 对于
[L, R], x ,[L, R] 的答案不小于x 当且仅当对于所有[l, r] 满足L \leq l \leq r \leq R ,均有[l, r] 区间的平均数不小于x 。
不难发现这个结论和 Hall 定理是等价的。
于是我们有式子:
其中
这个式子中的
可以发现每个点作为左端点和右端点时分别对应了一条直线,“
我们发现部分分正好有
进一步受到这个部分分启发,我们发现好像可以套一个分治。具体的,跑一遍猫树,假设当前区间
- 对
[L, mid] 跑一遍 Subtask4 的算法,得到[l, mid] 的答案; - 对
[mid + 1, R] 跑一遍 Subtask4 的算法,得到[mid + 1, r] 的答案; - 对
[l, mid] 中选左端点,[mid + 1, r] 中选右端点的情况求出最大的x 。
这三个值取
/* NE_Cat 4.15 */
#include <bits/stdc++.h>
#define lowbit(x) ((x) & (-(x)))
using namespace std;
const int N = 100010;
const long long INF = 2e16, mx = 400000000;
int n, m; long long a[N], b[N], pr_a[N], pr_b[N];
struct Range {int l, r, id;}; long long res[N];
int rt[N], idx, Root; long long f[N];
inline long long get_r(int p) {return pr_a[p] + pr_b[p];}
inline long long get_l(int p) {return pr_a[p - 1] + ((p == 1) ? 0 : pr_b[p - 2]);}
struct Line
{
long long k, b;
inline long long calc(long long x) {return k * x + b;}
};
inline Line get_line_l(int p) {return {1 - p, get_l(p)};}
inline Line get_line_r(int p) {return {-p, get_r(p)};}
struct SGT
{
int ls, rs; Line maxn, minn; bool tag_mx, tag_mn;
#define ls(x) tree[x].ls
#define rs(x) tree[x].rs
#define maxn(x) tree[x].maxn
#define minn(x) tree[x].minn
#define tag_mx(x) tree[x].tag_mx
#define tag_mn(x) tree[x].tag_mn
} tree[N * 60];
inline void insert_mx(int &now, int his, int l, int r, Line cur)
{
now = ++idx; tree[now] = tree[his];
if(!tag_mx(now)) {tag_mx(now) = true; maxn(now) = cur; return ;}
int mid = (l + r) >> 1;
if(maxn(now).calc(mid) < cur.calc(mid)) swap(maxn(now), cur);
if(l == r) return ;
if(cur.calc(l) > maxn(now).calc(l)) insert_mx(ls(now), ls(his), l, mid, cur);
if(cur.calc(r) > maxn(now).calc(r)) insert_mx(rs(now), rs(his), mid + 1, r, cur);
}
inline void insert_mn(int &now, int his, int l, int r, Line cur)
{
now = ++idx; tree[now] = tree[his];
if(!tag_mn(now)) {tag_mn(now) = true; minn(now) = cur; return ;}
int mid = (l + r) >> 1;
if(minn(now).calc(mid) > cur.calc(mid)) swap(minn(now), cur);
if(l == r) return ;
if(cur.calc(l) < minn(now).calc(l)) insert_mn(ls(now), ls(his), l, mid, cur);
if(cur.calc(r) < minn(now).calc(r)) insert_mn(rs(now), rs(his), mid + 1, r, cur);
}
inline long long query_mx(int now, int l, int r, int pos)
{
if(!now) return -INF;
long long res = (tag_mx(now) ? maxn(now).calc(pos) : -INF);
if(l == r) return res;
int mid = (l + r) >> 1;
if(pos <= mid) return max(res, query_mx(ls(now), l, mid, pos));
else return max(res, query_mx(rs(now), mid + 1, r, pos));
}
inline long long query_mn(int now, int l, int r, int pos)
{
if(!now) return INF;
long long res = (tag_mn(now) ? minn(now).calc(pos) : INF);
if(l == r) return res;
int mid = (l + r) >> 1;
if(pos <= mid) return min(res, query_mn(ls(now), l, mid, pos));
else return min(res, query_mn(rs(now), mid + 1, r, pos));
}
inline void solve(int l, int r, vector < Range > q)
{
int mid = (l + r) >> 1;
Root = idx = 0;
for(int i = mid + 1; i <= r; ++i)
{
insert_mx(Root, Root, 0, mx, {1 - i, get_l(i)});
long long l = 0, r = 4e8; f[i] = 0;
while(l <= r)
{
long long mid = (l + r) >> 1;
if((-i) * mid + get_r(i) >= query_mx(Root, 0, mx, mid)) f[i] = mid, l = mid + 1;
else r = mid - 1;
}
if(i > mid + 1) f[i] = min(f[i], f[i - 1]);
}
Root = idx = 0;
for(int i = mid; i >= l; --i)
{
insert_mn(Root, Root, 0, mx, {-i, get_r(i)});
long long l = 0, r = 4e8; f[i] = 0;
while(l <= r)
{
long long mid = (l + r) >> 1;
if((1 - i) * mid + get_l(i) <= query_mn(Root, 0, mx, mid)) f[i] = mid, l = mid + 1;
else r = mid - 1;
}
if(i < mid) f[i] = min(f[i], f[i + 1]);
}
idx = 0;
for(int i = l; i <= r; ++i) rt[i] = 0;
insert_mn(rt[mid + 1], rt[mid + 1], 0, mx, get_line_r(mid + 1));
for(int i = mid + 2; i <= r; ++i)
insert_mn(rt[i], rt[i - 1], 0, mx, get_line_r(i));
insert_mx(rt[mid], rt[mid], 0, mx, get_line_l(mid));
for(int i = mid - 1; i >= l; --i)
insert_mx(rt[i], rt[i + 1], 0, mx, get_line_l(i));
for(Range qy : q)
{
if(qy.l <= mid && qy.r > mid)
{
res[qy.id] = min(f[qy.l], f[qy.r]);
long long l = 0, r = 4e8, cur = 0;
while(l <= r)
{
long long mid = (l + r) >> 1;
if(query_mx(rt[qy.l], 0, mx, mid) <= query_mn(rt[qy.r], 0, mx, mid)) cur = mid, l = mid + 1;
else r = mid - 1;
}
res[qy.id] = min(res[qy.id], cur);
}
}
if(l == r) return ;
vector < Range > qL, qR;
for(Range qy : q)
{
if(qy.r <= mid) qL.push_back(qy);
if(qy.l > mid) qR.push_back(qy);
}
q.clear(); q.shrink_to_fit();
solve(l, mid, qL), solve(mid + 1, r, qR);
}
std::vector<int> testset(std::vector<int> A, std::vector<int> B, std::vector<int> L, std::vector<int> U)
{
n = A.size(); m = L.size();
for(int i = 1; i <= n; ++i) a[i] = A[i - 1];
for(int i = 1; i < n; ++i) b[i] = B[i - 1];
for(int i = 1; i <= n; ++i)
{
pr_a[i] = pr_a[i - 1] + a[i];
pr_b[i] = pr_b[i - 1] + b[i];
}
memset(res, 0x3f, sizeof(res));
vector < Range > q;
for(int i = 1; i <= m; ++i)
{
int l = L[i - 1], r = U[i - 1]; ++l, ++r;
if(l == r) res[i] = a[l] + b[l] + b[l - 1];
q.push_back({l, r, i});
}
solve(1, n, q);
vector < int > rec;
for(int i = 1; i <= m; ++i) rec.push_back(res[i]);
return rec;
}
/*
3 1
3 0 3
4 4
0 2
*/