种树 (堆模拟网络流)

种树

题目描述

cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。

输入输出格式

输入格式:

第一行,两个正整数n,k。

第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利。

输出格式:

输出1个数,表示cyrcyr种树的最大获利。

输入输出样例

输入样例#1: 复制

6 3
100 1 -1 100 1 -1

输出样例#1: 复制

200

说明

对于20%的数据,n<=20。

对于50%的数据,n<=6000。

对于100%的数据,n<=500000,k<=n/2,在一个地方种树获利的绝对值在1000000以内。

题解

一道非常好的堆模拟网络流题。
首先我们来想想\(O(n^2)\)
很显然的两重循环dp
第一重枚举第i个位置,第二重枚举选了j颗树。
对于O(n)的模拟。
我们首先明确选了 i 就不能选 i-1 和 i+1。
所以所有的情况都是由选 i 和选 i-1 ,i+1 产生的。
选 i 时, i 的贡献应当是大于 i-1 和 i+1 的。
但是当我们发现选 i-1 和 i+1 要更好时呢?
网络流有一个操作就是反悔。
我们是不是也可以反悔一下呢?
因为先选了 i ,那么下一次选择的时候,如果反悔就是选两边
就是选了周围两个点,不反悔就是选其他点
那么把周围两个点维护为一个点就可以了。
即 \(vi[i]=vi[l[i]]+vi[r[i]]-vi[i]\)
然后维护一下 \(l\) 和 \(r\) 数组。

代码

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
priority_queue<pair<int,int> >q;
int vis[1000001],ch[1000001],n,m;
long long ans;
struct node{
    int vi,id,l,r;
}t[1000001];
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
        ch[i]=t[i].vi=read();t[i].id=i;
        t[i].l=i-1;t[i].r=i+1;
        q.push(make_pair(ch[i],i));
    }
    t[0].r=1;t[n+1].l=n;
    while(m&&!q.empty()){
        while(vis[q.top().second])q.pop();
        int x=q.top().second;q.pop();
        if(t[x].vi<0)break;
        ans+=t[x].vi;
        t[x].vi=t[t[x].l].vi+t[t[x].r].vi-t[x].vi;
        vis[t[x].l]=vis[t[x].r]=1;
        t[x].l=t[t[x].l].l;t[t[x].l].r=x;
        t[x].r=t[t[x].r].r;t[t[x].r].l=x;
        q.push(make_pair(t[x].vi,x));
        m--;
    }
    printf("%lld",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/hhh1109/p/9571549.html

时间: 2024-10-07 09:20:57

种树 (堆模拟网络流)的相关文章

P1484 种树 - 堆 - 贪心

这题想得脑阔疼...我只想到可以选一个点,或者不选这个点选其左右两个点的和 先来特殊情况,k=1, 然后k=2 可以发现由1到2的过程中,可以是一个本来被选的点,替换为他左右两边的点,收益增加了a[pos+1] + a[pos-1] - a[pos] 这个题是一个个选,直到选了k个,有种递推的感觉,先确定种了前面几个,再确定这一个该怎么种 然后我不会处理若有别的点也选上,并且影响到这个pos+1和pos-1的情况 事实上可以把这三个点缩在一起啊(当然不能提前缩,要在pos这个点被选时缩起来) 然

[UOJ455][UER #8]雪灾与外卖——堆+模拟费用流

题目链接: [UOJ455]雪灾与外卖 题目描述:有$n$个送餐员(坐标为$x_{i}$)及$m$个餐厅(坐标为$y_{i}$,权值为$w_{i}$),每个送餐员需要前往一个餐厅,每个餐厅只能容纳$c_{i}$个送餐员,一个送餐员去一个餐厅的代价为$|x_{i}-y_{j}|+w_{j}$,求最小代价. 首先这个题可以暴力建图跑费用流,具体做法就不说了.现在我们考虑模拟费用流的过程,也就是模拟贪心及匹配中反悔的过程. 我们对送餐员和餐厅分别开一个小根堆然后从左往右决策每个坐标位置的人或餐厅的选择

CF 962(耻辱场) /2错误 相间排位 堆模拟 X轴距离最小值

A #include <bits/stdc++.h> #define PI acos(-1.0) #define mem(a,b) memset((a),b,sizeof(a)) #define TS printf("!!!\n") #define pb push_back #define inf 0x3f3f3f3f //std::ios::sync_with_stdio(false); using namespace std; //priority_queue<i

[dfs][模拟网络流] Luogu P4189 星际旅行

题目描述 公元30003000年,地球联盟已经攻占了银河系内的NN个星球,出于资金的考虑,政府仅仅在星球间建立了N-1N−1条双向时空隧道保证任意两个星球之间互相可达.出于管理上的考虑,第ii个星球的行政长官要求每个公民在一年内不得从该星球利用时空隧道次数超过H_iHi?次(这一统计是基于离开次数统计的,如果你已经使用从该星球离开过H_iHi?次,那么这一年内你就不能再使用时空隧道离开这个星球了).Louis Paosen是一个星际旅行家,他希望能使用尽量多次的时空隧道,但又不希望最终被迫定居的

NOIP 2012 提高组第二试模拟赛 Solution

第一题 题意 数据范围 Solution 三分求下凹函数最值 1 #include <cstdio> 2 #include <queue> 3 #include <iostream> 4 using namespace std; 5 inline void read(int &k) 6 { 7 k=0;int f=1;char c=getchar(); 8 while (c<'0'||c>'9')c=='-'&&(f=-1),c=ge

【Luogu】P2827蚯蚓(堆转队列)

按照国际惯例先发题目链接? woc从4月就开始做这sb题.最开始30分升到65分不管了,直到最近几天升到85分,再到今天AC.激动的心情自然是那些一遍就A或者一小时以内就A的神犇难以想象的. 下面说说主要几个分段. # 35分 按题意用堆模拟.每次暴力修改蚯蚓长度,于是get皮肤:TLE蓝. # 65分 考虑到暴力修改消耗的时间复杂度过大,于是考虑偷懒.既然我们不能暴力增长已经存进堆的蚯蚓长度,那就剪短将要存进堆的蚯蚓长度.将目前蚯蚓增加的长度记为mark,从堆里取出来的蚯蚓长度+mark,要存

BZOJ 1216 操作系统(堆)

用堆模拟题目中的操作即可. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath&

BZOJ 1150 CTSC2007 数据备份Backup 堆+贪心

题目大意:给定一个长度为n?1的序列,要求选出k个不相邻的数使得和最小 费用流显然能跑,而且显然过不去- - 考虑用堆模拟费用流 一个错误的贪心是每次取最小,这样显然过不去样例 我们把[每次取最小]改为[每次选择一个区间取反],用堆来维护这些区间即可 每次取出最小的区间,然后将两边合并 (比如现在堆里有[1,3][4,4][5,5])这三个区间,我取走了[4,4]并计入答案,那么我删除[1,3]和[5,5]这两个区间,并加入[1,5]这个区间,权值为[1,3]的权值+[5,5]的权值-[4,4]

【NOI赛前训练】——专项测试1&#183;网络流

T1: 题目大意: 传送门 给一个长度为$n(n<=200)$的数列$h$,再给$m$个可以无限使用的操作,第$i$个操作为给长度为花费$c_i$的价值给长度为$l_i$的数列子序列+1或-1,求将数列变为不下降数列的最小花费. 题解: 第一部分(上下界最小费用可行流): 设$h_0=-inf,h_{n+1}=inf$,令$a$为$h$的差分数组,即$a_i=h_{i}-h_{i-1}$.考虑当对于区间$[l,r]$操作时(比如+1),相当于$a_{r+1}$减少1,$a_{l}$增加1.若将$