A. Present 1700
最小值最大——>二分+贪心(一边差分一边前缀和)
#include <bits/stdc++.h>
using namespace std;
long long n, m, w, a[100005], ans, b[100005], c[100005], d[100005];
bool check(int x) {
long long rst = m;
for (long long i = 1; i <= n; i++) {
d[i] = a[i] - x;
b[i] = d[i] - d[i - 1];
c[i] = 0;
}
for (long long i = 1; i <= n; i++) {
c[i] = c[i - 1] + b[i];
if (c[i] < 0) {
b[i + 1] += -c[i];
b[min(i + w, n + 1)] += c[i];
rst += c[i];
}
}
if (rst < 0)
return 0;
else
return 1;
}
int main() {
scanf("%d%d%d", &n, &m, &w);
for (long long i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
long long l = 1, r = 1e9 + 5;
while (l <= r) {
long long mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
printf("%d", ans);
return 0;
}
B.Nested Segments 1800
二维偏序(排序降一维,树状数组降一维)
#include <bits/stdc++.h>
using namespace std;
struct S {
int l, r, id;
} a[200005];
int b[200005], ans[200005], tot, n;
struct BIT {
int C[200005];
int lowbit(int x) { return x & (-x); };
void add(int x) {
for (int i = x; i <= n; i += lowbit(i)) C[i]++;
}
int query(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += C[i];
return res;
}
} c;
bool cmp(S a, S b) { return a.l > b.l; }
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i].l, &a[i].r);
a[i].id = i;
b[++tot] = a[i].r;
}
sort(b + 1, b + tot + 1);
for (int i = 1; i <= n; i++) {
a[i].r = lower_bound(b + 1, b + tot + 1, a[i].r) - b;
}
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++) {
ans[a[i].id] = c.query(a[i].r);
c.add(a[i].r);
}
for (int i = 1; i <= n; i++) {
printf("%d\n", ans[i]);
}
return 0;
}
C. Two Teams 1800
支持任意删除的线性数据结构
1.并查集(路径压缩)(支持随机访问)
我的(丑陋):
#include <bits/stdc++.h>
using namespace std;
int n, k, a[200005], ans[200005];
struct S {
int id, val;
friend bool operator<(S a, S b) { return a.val < b.val; }
};
struct bcj {
int f[200005];
void init() {
for (int i = 1; i <= n; i++) f[i] = i;
f[n + 1] = n + 1;
}
int find(int x) {
if (f[x] != x)
return f[x] = find(f[x]);
return x;
}
} f1, f2;
priority_queue<S> q;
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
q.push({ i, a[i] });
}
f1.init(), f2.init();
int t = 0;
while (q.size()) {
int x = q.top().id;
q.pop();
if (ans[x])
continue;
int cnt = 0;
ans[x] = t + 1;
for (int i = x; i <= n && f1.find(i + 1) <= n; i = f1.find(i + 1)) {
f1.f[i] = f1.find(i + 1);
f2.f[f1.find(i + 1)] = i;
if (ans[f1.find(i + 1)] == 0)
ans[f1.find(i + 1)] = t + 1, cnt++;
if (cnt == k)
break;
}
cnt = 0;
for (int i = x; i >= 1 && f2.find(i - 1) >= 1; i = f2.find(i - 1)) {
f1.f[f2.find(i - 1)] = i;
f2.f[i] = f2.find(i - 1);
if (ans[f2.find(i - 1)] == 0)
cnt++, ans[f2.find(i - 1)] = t + 1;
if (cnt == k) {
break;
}
}
t ^= 1;
}
for (int i = 1; i <= n; i++) {
printf("%d", ans[i]);
}
return 0;
}
2.链表(短+效率高)
#include <cstdio>
#include <queue>
using namespace std;
const int N = 2e5 + 50;
typedef pair<int, int> pr;
int n, K, opt, a[N], vis[N], pre[N], net[N];
priority_queue<pr> q, p;
int main() {
scanf("%d%d", &n, &K);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
pre[i] = i - 1;
net[i] = i + 1; //初始化前驱后继
q.push(pr(a[i], i));
}
net[n] = 0;
while (!q.empty()) {
while (p.size() && q.top() == p.top()) q.pop(), p.pop();
int i = 0, j = 0, k = 0;
if (q.empty())
break; //堆顶相同删除
for (i = 1, j = net[q.top().second]; i <= K && j; i++, j = net[j])
vis[j] = opt + 1, p.push(pr(a[j], j)); //后继
for (i = 1, k = pre[q.top().second]; i <= K && k; i++, k = pre[k])
vis[k] = opt + 1, p.push(pr(a[k], k)); //前驱
vis[q.top().second] = opt + 1;
q.pop();
pre[j] = k;
net[k] = j;
opt ^= 1; //同时维护前驱后继
}
for (int i = 1; i <= n; i++) printf("%d", vis[i]);
}
补充:十字链表
3.set(好写)也没那么好写
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> pii;
int a[200005];
int ans[200005];
signed main() {
set<int> s;
set<pii> st;
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> a[i];
st.insert({ -a[i], i });
s.insert(i);
}
s.insert(-1);
s.insert(n);
int th = 1;
while (!st.empty()) {
pii p = *st.begin();
st.erase(st.begin());
int x = p.second;
ans[x] = th;
s.erase(x);
for (int _ = 0; _ < k and ~x; _++) {
x = *--s.lower_bound(x);
if (~x) {
s.erase(x);
st.erase({ -a[x], x });
ans[x] = th;
}
}
x = p.second;
for (int _ = 0; _ < k and x != n; _++) {
x = *s.lower_bound(x);
if (x != n) {
s.erase(x);
st.erase({ -a[x], x });
ans[x] = th;
}
}
th ^= 3;
}
for (int i = 0; i < n; i++) cout << ans[i];
return (0 ^ 0);
}
D. Max GEQ Sum 1800
列举内容Li,Ri,Sumi,j,Maxi
显然 Maxi所含信息量最小,故按此分类
设当前取值为i,则i的作用范围再左侧第一个比他大的数和右侧第一个比他大的数之间,正是单调栈所擅长的(划重点)
而Sumi,j显然选取最大的,考虑应用前缀和,在区间范围内找到右侧最大前缀和,左侧最小前缀和,即得最大区间和,而这又可以通过ST表等求RMQ得到
可以看出,按影响求解,按Maxi分类,便省掉了Li,Ri,得到质的优化(本质)
#include <bits/stdc++.h>
using namespace std;
long long n, a[200005], L[200005], R[200005], STT[200005][25], SST[200005][25], sum[200005],
lg[200005];
long long T;
void initST() {
STT[1][0] = sum[1];
SST[1][0] = sum[1];
for (long long i = 2; i <= n; i++) {
lg[i] = lg[i / 2] + 1;
STT[i][0] = sum[i];
SST[i][0] = sum[i];
}
for (long long i = 1; i <= lg[n] + 1; i++) {
for (long long j = 1; j + (1 << i) - 1 <= n; j++) {
STT[j][i] = max(STT[j][i - 1], STT[j + (1 << i - 1)][i - 1]);
SST[j][i] = min(SST[j][i - 1], SST[j + (1 << i - 1)][i - 1]);
}
}
}
long long RMQ2(long long i, long long j) {
long long c = lg[j - i + 1];
return max(STT[i][c], STT[j - (1 << c) + 1][c]);
}
long long RMQ3(long long i, long long j) {
long long c = lg[j - i + 1];
return min(SST[i][c], SST[j - (1 << c) + 1][c]);
}
bool pd() {
for (long long i = 1; i <= n; i++) {
if (a[i] < RMQ2(i, R[i]) - RMQ3(L[i] - 1, i - 1)) {
return 0;
}
}
return 1;
}
int main() {
scanf("%lld", &T);
while (T--) {
scanf("%lld", &n);
for (long long i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
stack<long long> st;
for (long long i = 1; i <= n; i++) {
while (st.size() && a[st.top()] <= a[i]) {
R[st.top()] = i - 1;
st.pop();
}
if (st.empty())
L[i] = 1;
else
L[i] = st.top() + 1;
st.push(i);
}
for (long long i = 1; i <= n; i++) {
if (R[i] == 0)
R[i] = n;
}
initST();
if (pd()) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
E. Optimal Sum 2000
真就是按题意模拟,先考虑最大值,可以用两个set进行模拟,一个表示被选集合,另一个表示待选集合,滑动窗口进行模拟。
#include <bits/stdc++.h>
using namespace std;
long long n, len, k, a[100005], b[100005], ans = -1e9;
void solve(long long *a) {
long long sum = 0, sum1 = 0;
multiset<long long> s, s1;
for (int i = 1; i <= len; i++) {
if (a[i] < 0 && k) {
if (s.size() < k) {
sum1 += a[i] * 2;
s.insert(a[i]);
} else if (a[i] <= *s.rbegin()) { //a[i]可以存活更久,故取等号
sum1 += a[i] * 2 - *s.rbegin() * 2;
s1.insert(*s.rbegin());
s.erase(*s.rbegin());
s.insert(a[i]);
} else {
s1.insert(a[i]);
}
}
sum += a[i];
}
ans = max(ans, sum - sum1);
for (int i = len + 1; i <= n; i++) {
if (s.find(a[i - len]) != s.end()) {
s.erase(s.find(a[i - len]));
sum1 -= a[i - len] * 2;
}
if (s1.find(a[i - len]) != s1.end())
s1.erase(s1.find(a[i - len]));
while (s.size() < k && s1.size()) {
s.insert(*s1.begin());
sum1 += *s1.begin() * 2;
s1.erase(s1.begin());
}
if (a[i] < 0 && k) {
if (s.size() < k) {
sum1 += a[i] * 2;
s.insert(a[i]);
} else if (a[i] <= *s.rbegin()) {
sum1 += a[i] * 2 - *s.rbegin() * 2;
s1.insert(*s.rbegin());
s.erase(*s.rbegin());
s.insert(a[i]);
} else {
s1.insert(a[i]);
}
}
sum += a[i];
sum -= a[i - len];
ans = max(ans, sum - sum1);
}
}
int main() {
cin >> n >> len;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
b[i] = -a[i];
}
cin >> k;
solve(a);
solve(b);
printf("%lld", ans);
return 0;
}