P4064 [JXOI2017] 加法 题解
思路
一眼二分答案,这种区间的题很难不排序,可以考虑这个贪心 check:
区间左端点升序排序之后,每次遇到一个点,判断这个点是否合法,如果不合法就在所有左端点在这个点左边的区间里选择右端点最大的一个。
感性证明:这个点之前的点已经保证合法了,所有左端点在这个点左侧的区间左端点到这个点这部分只对当前点造成贡献,对后面无影响,所以显然选择右端点最大的可以让后面吃到最多的贡献。
然后就……做完了……
时间复杂度:\(O(n\log n\log V)\)。
// Problem: P4064 [JXOI2017] 加法
// Contest: Luogu
// Author: Moyou
// Copyright (c) 2024 Moyou All rights reserved.
// Date: 2024-02-02 22:33:01
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int n, m, k, v, a[N];
pair<int, int> p[N];
priority_queue<pair<int, int> > heap;
vector<int> t[N];
struct BIT {
int tr[N];
void update(int i, int c) { for (; i <= n + 1; i += i & -i) tr[i] += c; }
void update(int i) { for (; i <= n + 1; i += i & -i) tr[i] = 0; }
int query(int i) { int res = 0; for (; i; i &= i - 1) res += tr[i]; return res; }
void clear() {memset(tr, 0, sizeof tr);}
} bit;
bool check(int mid) {
while(heap.size()) heap.pop();
for(int i = 1; i <= n; i ++) bit.update(i);
int cnt = 0;
for(int i = 1; i <= n; i ++) {
for(auto j : t[i]) heap.push({p[j].second, j});
while(cnt < k && bit.query(i) + a[i] < mid) {
if(heap.empty()) return 0;
int t = heap.top().second; heap.pop();
bit.update(p[t].first, v), bit.update(p[t].second + 1, -v);
cnt ++;
}
if(bit.query(i) + a[i] < mid) return 0;
}
return 1;
}
void work() {
cin >> n >> m >> k >> v;
for(int i = 1; i <= n; i ++) cin >> a[i], t[i].clear();
for(int i = 1; i <= m; i ++) cin >> p[i].first >> p[i].second, t[p[i].first].push_back(i);
int l = 1, r = 1e8, ans = 0;
while(l <= r) {
int mid = l + r >> 1;
if(check(mid)) l = mid + 1, ans = mid;
else r = mid - 1;
}
cout << ans << '\n';
return ;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) work();
return 0;
}
标签:JXOI2017,int,题解,mid,端点,P4064,include
From: https://www.cnblogs.com/MoyouSayuki/p/18004164