反悔贪心乱做
解决历史遗留问题.jpg
[APIO/CTSC2007]数据备份
显然,选的一定是相邻的两个城市。 所以问题转化成,给一个差分数组,选 个不相邻的数,使得和最小。
这就是经典的种树的问题。
我们每次贪心的选最大值,然后把左右两边的全删掉,并再插入 ,这样再选到它的时候意味着选两边的,不选中间的,而且再左右的也不选。这样重复 次即可。
CF730I
这个诡异的数据范围应该是留给费用流的。
就是源点连向各个学生,费用为 ,流量为 ,每个学生连向两个点,分别代表两个社团,流量为 ,费用为负的权值,然后这两个点再连向汇点,流量分别为 。
首先我们将 值最大的 个人选出来,然后考虑选出另外一个社团的人。假设我们选的是还没有加入任何社团的人,那就直接选 最大的就行。 否则我们从编程社团选一个人到体育社团,然后从无业游民里选一个到编程社团,那贡献就是 ,那就再开两个堆表示 ,和 ,然后直接做就行。注意判重, 和 的堆需要记录不是任何社团的,而 的不需要管,因为一定是编程社团的。
[NOI2017] 蔬菜
不说这题的反悔贪心的做法(我觉得没有这种有意思还不好写).
就说有一个弱化版的题 UVA316 Supermarket
就是有一个神仙的想法就是先按价值排序,然后加入到消失点前里消失点最近的位置。这样能保证尽可能的多放物品,如果加不进去,就说明这个物品没啥用,直接扔了就行。
这题也是一样,对于每种蔬菜在大根堆里,先插入 ,然后取出最大的,也是放到可行的尽可能后的位置,如果不存在这样的位置,这种蔬菜就直接扔掉就好啦(所以保证复杂度),然后如果这种蔬菜还能放,就在堆里插入 。
询问的时候就直接问插入 个蔬菜时的价值和即可。我们刚才考虑的是最坏情况,也就是对每种蔬菜尽可能往后放,这样的目的是让别的蔬菜能放进去。而询问的时候前面就是有 个空位,原来那些蔬菜是放在后面的,现在全部堆到前面也不成问题,所以不用在意时间的问题。
[NOI2019] 序列
经典:
这不是普及组套路吗
先贪心的对于两个序列都把最大的 个选出来,然后每次都让交集大小增加 。
按照反悔贪心的套路,我们分四种情况讨论。
- 选一个 b 选了的,a 没选的下标 ,选一个 选了的, 没选的下标 。贡献为 。就是将 里选一个 ,并且把 从 里丢掉
- 选一个 选了的, 没选的下标 ,选一个 选了的, 没选的下标 。贡献为 。
- 选一个 选 不选的 , 选 不选的 ,两个都没选的 ,把 丢掉,然后两个都选 ,贡献为
- 选一个 选 不选的 , 选 不选的 ,两个都选的 ,把 都丢掉,然后 选 , 选 。
然后就是按照套路,开 6 个堆,维护上面说的那些信息,然后就没了。
其实想通了不是很难喵。
#include <bits/stdc++.h>
using namespace std;
int n;
typedef long long ll;
const int maxn=200030;
int a[maxn],b[maxn],idx1[maxn],idx2[maxn];bool typ1[maxn],typ2[maxn];int op[maxn];
struct node
{
int val,idx;
bool operator<(const node &x)const{
return val<x.val;
}
};
priority_queue<node> Q1,Q2,Q3,Q4,Q5,Q6;
void add(int i)
{
if(op[i]==1) Q6.push((node){-(a[i]+b[i]),i});
if(op[i]==2) Q2.push((node){-a[i],i}),Q3.push((node){b[i],i});
if(op[i]==3) Q1.push((node){a[i],i}),Q4.push((node){-b[i],i});
if(op[i]==4) Q5.push((node){a[i]+b[i],i});
}
bool cmp1(int x,int y){return a[x]>a[y];}
bool cmp2(int x,int y){return b[x]>b[y];}
int main()
{
// freopen("p.in","r",stdin);
int TT; scanf("%d",&TT);
while(TT--)
{
memset(typ1,0,sizeof(typ1));memset(typ2,0,sizeof(typ2));
while(Q1.size()) Q1.pop();while(Q2.size()) Q2.pop();while(Q3.size()) Q3.pop();
while(Q4.size()) Q4.pop();while(Q5.size()) Q5.pop();while(Q6.size()) Q6.pop();
int n,K,L;scanf("%d%d%d",&n,&K,&L);
ll ans=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),idx1[i]=i;
for(int i=1;i<=n;i++)
scanf("%d",&b[i]),idx2[i]=i;
sort(idx1+1,idx1+1+n,cmp1); sort(idx2+1,idx2+1+n,cmp2);
for(int i=1;i<=K;i++)
typ1[idx1[i]]=typ2[idx2[i]]=1,ans+=a[idx1[i]],ans+=b[idx2[i]];
int cnt=0;
for(int i=1;i<=n;i++)
{
if(typ1[i]&&typ2[i]) op[i]=1,cnt++,Q6.push((node){-(a[i]+b[i]),i});
if(typ1[i]&&!typ2[i]) op[i]=2,Q2.push((node){-a[i],i}),Q3.push((node){b[i],i});
if(!typ1[i]&&typ2[i]) op[i]=3,Q1.push((node){a[i],i}),Q4.push((node){-b[i],i});
if(!typ1[i]&&!typ2[i]) op[i]=4,Q5.push((node){a[i]+b[i],i});
}
for(int i=1;i<=L-cnt;i++)
{
while(Q1.size()&&op[Q1.top().idx]!=3) Q1.pop();
while(Q2.size()&&op[Q2.top().idx]!=2) Q2.pop();
while(Q3.size()&&op[Q3.top().idx]!=2) Q3.pop();
while(Q4.size()&&op[Q4.top().idx]!=3) Q4.pop();
while(Q5.size()&&op[Q5.top().idx]!=4) Q5.pop();
while(Q6.size()&&op[Q6.top().idx]!=1) Q6.pop();
ll x1=Q1.size()?Q1.top().val:-1e15;ll x2=Q2.size()?(-Q2.top().val):1e15;
ll x3=Q3.size()?Q3.top().val:-1e15;ll x4=Q4.size()?(-Q4.top().val):1e15;
ll x5=Q5.size()?Q5.top().val:-1e15;ll x6=Q6.size()?(-Q6.top().val):1e15;
ll ans1=x1-x2; ll ans2=x3-x4; ll ans3=x5-x2-x4; ll ans4=x1+x3-x6;
ll mx=max({ans1,ans2,ans3,ans4});
ans+=mx;
if(ans1==mx)
{
int x=Q1.top().idx; int y=Q2.top().idx;
op[x]=1; op[y]=4;add(x); add(y);
}
else if(ans2==mx)
{
int x=Q3.top().idx; int y=Q4.top().idx;
op[x]=1; op[y]=4; add(x); add(y);
}
else if(ans3==mx)
{
int x=Q2.top().idx; int y=Q4.top().idx; int z=Q5.top().idx;
op[x]=4,op[y]=4; op[z]=1;
add(x);add(y);add(z);
}
else
{
int x=Q1.top().idx; int y=Q3.top().idx; int z=Q6.top().idx;
op[x]=1,op[y]=1,op[z]=4;
add(x); add(y); add(z);
}
}
printf("%lld\n",ans);
}
}
本文作者:cc0000
本文链接:https://www.cnblogs.com/cc0000/p/16942839.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步