把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

反悔贪心复习笔记

讲解

本质上就是先贪心选择,遇到更优的再换

P1484 种树

题目概述

给你 \(n\) 个位置,每个位置上有权值 \(a_i\),你至多选择 \(m\) 个位置两两不相邻,求你得到的最大权值。

分析

首先设 \(l_i,r_i\) 表示旁边两个位置。

用大根堆存值和下标。

如果我当前选择了 \((val,i)\)(如果 \(val\leq 0\) 就直接退出,因为是至多选 \(m\) 个),按照下面的步骤进行:

  • 让答案 \(ans+val\)
  • \(a_i=a_{l_i}+a_{r_i}-a_i\),作为反悔,因为下一次如果选到这个值,就说明我不要 \(i\),而要另外的两个。
  • \((a_i,i)\) 放入队列中(记得删除队列中标记不能选择的值)。
  • 执行 del(i) 函数
    • 标记 \(l_i,r_i\) 不可选择,令 \(l_i=l_{l_i},r_i=r_{r_i}\),因为下一次选择这个值的时候不可选的就是另外两个的了(可以看作把他们三个合并成一个了)。
    • \(l_{r_i}=i,r_{l_i}=i\)

为什么可以合并呢?

假设 \(5\) 个数 \([a,b,c,d,e]\),你先选择了 \(c\),然后将 \([b,c,d]\) 合并成 \(b+d-c\),下一次答案加这个值的时候就少了 \(c\)

所以序列变成了 \([a,b+d-c,e]\),你又选择了 \(b+d-c\),那么所以将 \([a,b+d-c,e]\) 合并成 \(a+e-b-d+c\),是不是中间的那个又选上了,而且这个选的次数是恰好递增的。

因为是合并,所以我们的标记数组是不会变的。

代码

时间复杂度 \(\mathcal{O}(m\log n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <queue>
#define int long long
#define PII pair<int,int>
#define N 300005
using namespace std;
int n,k,a[N],l[N],r[N];
bool vis[N];
priority_queue<PII> q;
void del(int id) {
    vis[l[id]] = vis[r[id]] = 1;
    l[id] = l[l[id]];
    r[id] = r[r[id]];
    r[l[id]] = id;
    l[r[id]] = id;
}
signed main(){
    cin >> n >> k;
    for (int i = 1;i <= n;i ++) scanf("%lld",&a[i]),l[i] = i - 1,r[i] = i + 1;
    for (int i = 1;i <= n;i ++) q.push({a[i],i});
    int ans = 0;
    for (int i = 1;i <= k;i ++) {
        while(vis[q.top().second]) q.pop();
        auto t = q.top();
        q.pop();
        if (t.first <= 0) break;
        ans += t.first;
        a[t.second] = a[l[t.second]] + a[r[t.second]] - a[t.second];
        q.push({a[t.second],t.second});
        del(t.second);
    }
    cout << ans;
    return 0;
}  

P1792 [国家集训队] 种树

题目概述

更上一题差不多,只不过要求了必须要 \(m\) 个,且序列是一个环

分析

同理。

代码

时间复杂度 \(\mathcal{O}(m\log n)\)

#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define int long long
#define N 200005
#define PII pair<int,int>
using namespace std;
int n,m,a[N],l[N],r[N];
bool vis[N];
priority_queue<PII> q;
void del(int id) {
    vis[l[id]] = vis[r[id]] = 1;
    l[id] = l[l[id]],r[id] = r[r[id]];
    r[l[id]] = id,l[r[id]] = id;
}
signed main(){
    cin >> n >> m;
    if (m > n / 2) return cout << "Error!",0;
    for (int i = 1;i <= n;i ++) scanf("%lld",&a[i]),l[i] = i - 1,r[i] = i + 1,q.push({a[i],i});
    r[n] = 1;
    l[1] = n;
    int ans = 0;
    for (int i = 1;i <= m;i ++) {
        while(vis[q.top().second]) q.pop();
        auto t = q.top();
        q.pop();
        ans += t.first;
        a[t.second] = a[l[t.second]] + a[r[t.second]] - a[t.second];
        q.push({a[t.second],t.second});
        del(t.second);
    }
    cout << ans;
    return 0;
}
posted @ 2025-10-14 10:38  high_skyy  阅读(13)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end