洛谷P1484 种树
解法有wqs二分和反悔贪心
反悔贪心一般的形式就是通过一个优先队列来贪心
做法很巧妙,优先队列维护最大值,当你拿了 \(a_i\),显然只可能 \(a_{i-1} + a_{i + 1} > a_i\),那么可以把 \(a_{i-1}+a_{i+1}-a_i\)看作一个整体再放进去
int a[maxn], l[maxn], r[maxn];
bool vis[maxn];
void run() {
int n, k; scanf("%d %d", &n, &k);
priority_queue<pair<ll, int>> q;
for(int i = 1; i <= n; ++ i) {
scanf("%d", &a[i]);
l[i] = i - 1;
r[i] = i + 1;
q.push({a[i], i});
}
l[1] = 1; r[n] = n;
ll ans = 0;
while(k --) {
while(vis[q.top().second]) q.pop();
pair<ll, int> top = q.top(); q.pop();
if(top.first < 0) break;
ans += top.first; int id = top.second;
a[id] = a[l[id]] + a[r[id]] - a[id];
top.first = a[id];
vis[l[id]] = vis[r[id]] = 1;
l[id] = l[l[id]]; r[id] = r[r[id]];
if(id != l[id]) r[l[id]] = id;
if(id != r[id]) l[r[id]] = id;
q.push(top);
}
printf("%lld\n", ans);
return ;
}
题目中有约束条件是小于k棵树的最大值,也符合凸函数的特性
可以通过wqs二分求解凸函数最优解
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;
ll dp[maxn][2];
int a[maxn], n, k;
void run() {
int n, k; scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++ i) {
scanf("%d", &a[i]);
for(int j = k; j >= 0; -- j) {
dp[j][0] = max(dp[j][0], dp[j][1]);
if(j) dp[j][1] = dp[j - 1][0] + a[i];
}
}
printf("%lld\n", max(dp[k][0], dp[k][1]));
}
int g[maxn][2];
pair<ll, int> run1(int c) {
for(int i = 1; i <= n; ++ i) {
dp[i][1] = dp[i - 1][0] + a[i] + c;
g[i][1] = g[i - 1][0] + 1;
if(dp[i - 1][1] > dp[i - 1][0]) dp[i][0] = dp[i - 1][1], g[i][0] = g[i - 1][1];
else if(dp[i - 1][1] < dp[i - 1][0]) dp[i][0] = dp[i - 1][0], g[i][0] = g[i - 1][0];
else dp[i][0] = dp[i - 1][0], g[i][0] = min(g[i - 1][0], g[i - 1][1]);//拿最少//max(g[i - 1][0], g[i - 1][1]);//拿最多
}
ll ans; int cnt;
if(dp[n][0] > dp[n][1]) ans = dp[n][0], cnt = g[n][0];
else if(dp[n][0] < dp[n][1]) ans = dp[n][1], cnt = g[n][1];
else ans = dp[n][1], cnt = min(g[n][0], g[n][1]);//拿最少//cnt = max(g[n][0], g[n][1]);//拿最多
return pair<ll, int>{ans, cnt};
//不能拿最多的,会错,当然如果判断[l(c), r(c)]当然是可以对的
}
void solve() {
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
pair<ll, int> res = run1(0);
if(res.second <= k) {//大于0的比k个少
printf("%lld\n", res.first);
return ;
}
int l = -1000000, r = -1;
while(l <= r) {
int mid = l + r >> 1;
pair<ll, int> res = run1(mid);//二分的含义是所有值减去mid后的 最大值中拿的个数最少
if(res.second <= k) l = mid + 1;//求得的cnt是l(c)
else r = mid - 1;
}
res = run1(l - 1);
printf("%lld\n", res.first - 1ll * (l - 1) * k);
}
signed main() {
int t = 1; //scanf("%d", &t);
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号