20250205数据结构综合-S(少补7题)

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值