• [学习笔记] 反悔贪心


    总结

    这东西直接刷题吧。根据我做过的题有下列几个方法:

    • 先乱贪心,然后设计反悔机制来修正答案。
    • 先建出网络流模型,然后研究性质(凸凹性)
    • 先建出费用流模型,然后模拟费用流(网络流的本质也是反悔贪心)

    这东西和网络流关系密切,很多时候要结合着用。

    UPD20217/7/17:今天 ( t cf) 的一道反悔贪心把我干傻了,我补充一下如何判断设计的反悔贪心是否正确。

    种树

    题目描述

    点此看题

    解法

    据花姐姐说可以直接 (wqs) 二分,这种时候直接感性理解就好(这不是重点)

    我们首先考虑一种乱贪心,也就是直接拿当前权值最大的,然后删去相邻的就可以了。

    考虑这种贪心为什么会错,就是因为我们不一定选最大的,而可能同时选他相邻的两个

    反悔贪心的思想就是,我们先按照乱贪心的方法做,然后通过反悔来修正答案,这就需要我们根据乱贪心出现的问题来设计反悔机制。比如这道题,我们可以设计反悔机制为:再选一次当前这个数,代表反悔之前的选择,而选择旁边的两个数。

    现在维护这个反悔机制就行了,我们可以维护一个双向链表提供每个位置两边的数,然后每次就直接优先队列找权值最大的位置,找到之后删除旁边的两个数,再更新这个点 (t) 的权值为 (a[l[t]]+a[r[t]]-a[t]),更新一下双端队列后把这个数放回优先队列就行了。

    但是还有一点细节的问题,戳这里

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int M = 500005;
    #define db double
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,k,ans,a[M],l[M],r[M],vis[M];
    struct node
    {
    	int x,c;
    	bool operator < (const node &b) const
    	{
    		return c<b.c;
    	}
    };priority_queue<node> q;
    int main()
    {
    	n=read();k=read();
    	if(n<k*2)
    	{
    		puts("Error!");
    		return 0;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=read();
    		q.push(node{i,a[i]});
    		l[i]=i-1;r[i]=i+1;
    	}
    	l[1]=n;r[n]=1;
    	while(!q.empty())
    	{
    		int t=q.top().x;q.pop();
    		if(vis[t]) continue;
    		vis[l[t]]=vis[r[t]]=1;
    		ans+=a[t];
    		a[t]=a[l[t]]+a[r[t]]-a[t];
    		k--;
    		if(k==0) break;
    		//更新双向链表
    		l[t]=l[l[t]];
    		r[t]=r[r[t]];
    		r[l[t]]=t;
    		l[r[t]]=t;
    		q.push(node{t,a[t]});
    	}
    	printf("%d
    ",ans);
    }
    

    建筑抢修

    题目描述

    点此看题

    解法

    典型的反悔贪心。考虑乱贪心就是按 (T_2) 排序,每次就按着结束时间修建筑,能修就修。

    会出的问题就是可以结束时间早的会占用很多时间,把后面的时间挤压了。那么我们考虑每次替换成时间消耗更小的,也就是维护一个优先队列考虑替换队首就行了。

    #include <cstdio>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int M = 150005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,sum;priority_queue<int> q;
    struct node
    {
    	int x,y;
    	bool operator < (const node &b) const
    	{
    		return y<b.y;
    	}
    }a[M];
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		int x=read(),y=read();
    		a[i]=node{x,y};
    	}
    	sort(a+1,a+1+n);
    	for(int i=1;i<=n;i++)
    	{
    		int x=a[i].x;
    		if(sum+x<=a[i].y)//可以直接修
    		{
    			q.push(x);
    			sum+=x;
    		}
    		else if(x<q.top())
    		{
    			sum-=q.top();
    			q.pop();
    			q.push(x);
    			sum+=x;
    		}
    	}
    	printf("%d
    ",q.size());
    }
    

    CF802O April Fools' Problem

    题目描述

    点此看题

    解法

    这个题真的是个妙妙题。

    首先不难建出网络流模型,源点向 (a_i) 连边,(a_i)(b_i) 连边,(b_i) 向汇点连边,相邻两个 (a_i,a_{i+1}) 连边。但是因为数据规模太大所以说无脑费用流是跑不动的。

    看到 恰好选k个 类似的字眼就想一想 ( t wqs) 二分吧,这道题打印 (k) 道题就相当于恰好选 (k) 个嘛。但是 ( t wqs) 还需要有凸单调性,从网络流的角度考虑,每次的最短路都在增加,也就是增量是递增的,那么 ( t wqs) 二分就没问题了。

    我们考虑在 (b_i) 上面加权,现在问题变成了无限制 (a,b) 匹配的最小代价,这个可以直接用反悔贪心实现,也就是我们在 (b_i) 和前面最小的 (a) 匹配之后也丢进优先队列,在后面的选择中可以反悔。

    #include <cstdio>
    #include <queue>
    using namespace std;
    #define int long long
    const int M = 500005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,res,num,a[M],b[M];
    struct node
    {
    	int c,x;
    	bool operator < (const node &b) const
    	{
    		return c>b.c;
    	}
    };
    void work(int mid)
    {
    	priority_queue<node> q;
    	res=num=0;
    	for(int i=1;i<=n;i++)
    	{
    		int x=b[i]-mid;
    		q.push(node{a[i],1});
    		node t=q.top();
    		if(x+t.c<=0)
    		{
    			q.pop();//取了再弹 
    			res+=x+t.c;
    			num+=t.x;
    			q.push(node{-x,0});//供反悔 
    		}
    	}
    }
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read();
    	for(int i=1;i<=n;i++)
    		b[i]=read();
    	int l=0,r=2e9;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		work(mid);
    		if(num>=m)
    		{
    			ans=res+mid*m;
    			r=mid-1;
    		}
    		else l=mid+1;
    	}
    	printf("%lld
    ",ans);
    }
    

    CF436E Cardboard Box

    题目描述

    点此看题

    解法

    虽然这道题没有用反悔贪心我还是把他加上来了

    先模一下 Ark 巨佬,这个方法是真的顶。

    先把所有 (b_i) 减去 (a_i) 得到新的 (b_i),也就是拿第二颗星需要多付出的代价(下文 (b_i) 都是这意思)

    考虑将物品分类,第一类 (a_i<b_i),第二列 (a_igeq b_i),可以将两类物品分别求解再合并答案。

    对于第一类物品,因为一定会先取 (a_i) 再取 (b_i),所以直接把所有东西混在一起排序即可,记 (g(x)) 表示选了 (x) 颗星星的最大价值,那么一定是选取排序后数组的一个前缀。

    对于第二类物品,因为此时选取 (a_i) 的目的是选取对应的 (b_i),那么不难导出一个结论:可以一组一组地选物品,至多只有一组只选了一个 (a_i),那么做法就呼之欲出了,我们把每组按 (a_i+b_i) 排序,记 (f(x)) 表示选了 (x) 星星的最大价值。那么 (f(2x)) 就直接拿前 (i) 组即可,(f(2x+1)) 就拿前 (i) 组再加上后面的 (a_i),或者是拿前 (i+1) 组再除去一个前面的 (b_i)

    因为只用算一个位置的值,所以直接枚举第一类物品选的星星个数即可,时间复杂度 (O(nlog n))

    还有一个加强版,如果要算 (forall win[1,2n]) 的最优解的话,因为 (g(x)) 是个凸函数,所以可以用决策单调性优化 (max) 卷积,那么可以做到 (O(nlog n)) 啦!

    还有这道题的结论是真的强,我已经是第二次见到了,是优化背包的经典结论吧!

    ( t luogu) 上说要算的是选取星数大于等于 (w) 的最优解,那你再把 (w+1) 算一次不就行了么?

    下面写了一个暴力反悔贪心的代码....

    #include <cstdio>
    #include <cassert>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 300005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,a[M],b[M],vis[M];
    struct n1
    {
    	int x;
    	n1(int X=0) : x(X) {}
    	bool operator < (const n1 &r) const
    	{
    		return a[x]>a[r.x];
    	}
    };
    struct n2
    {
    	int x;
    	n2(int X=0) : x(X) {}
    	bool operator < (const n2 &r) const
    	{
    		return b[x]>b[r.x];
    	}
    };
    struct n3
    {
    	int x;
    	n3(int X=0) : x(X) {}
    	bool operator < (const n3 &r) const
    	{
    		return a[x]+b[x]>a[r.x]+b[r.x];
    	}
    };
    struct n4
    {
    	int x;
    	n4(int X=0) : x(X) {}
    	bool operator < (const n4 &r) const
    	{
    		return a[x]<a[r.x];
    	}
    };
    struct n5
    {
    	int x;
    	n5(int X=0) : x(X) {}
    	bool operator < (const n5 &r) const
    	{
    		return b[x]<b[r.x];
    	}
    };
    signed main()
    {
    	n=read();m=read();
    	priority_queue<n1> q1;
    	priority_queue<n2> q2;
    	priority_queue<n3> q3;
    	priority_queue<n4> q4;
    	priority_queue<n5> q5;
    	a[0]=b[0]=1e12;
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=read();b[i]=read()-a[i];
    		q1.push(n1(i));
    		q3.push(n3(i));
    	}
    	while(m>0)
    	{
    		while(!q1.empty() && vis[q1.top().x]!=0) q1.pop();
    		while(!q2.empty() && vis[q2.top().x]!=1) q2.pop();
    		while(!q3.empty() && vis[q3.top().x]!=0) q3.pop();
    		while(!q4.empty() && vis[q4.top().x]!=1) q4.pop();
    		while(!q5.empty() && vis[q5.top().x]!=2) q5.pop();
    		//
    		int x=q1.empty()?0:q1.top().x;
    		int y=q2.empty()?0:q2.top().x;
    		int z=q3.empty()?0:q3.top().x;
    		int xx=q4.empty()?0:q4.top().x;
    		int yy=q5.empty()?0:q5.top().x;
    		int c1=a[x],c2=b[y],c3=a[z]+b[z],f=0;
    		//
    		if(xx) c3-=a[xx],f=1;
    		if(yy && a[z]+b[z]-b[yy]<=c3)
    			c3=a[z]+b[z]-b[yy],f=2;
    		int mi=min(c1,min(c2,c3));
    		//
    		m--;ans+=mi;
    		if(mi==c1)//a
    		{
    			assert(mi==a[x]);
    			vis[x]=1;
    			q2.push(n2(x));
    			q4.push(n4(x));
    		}
    		else if(mi==c2)//b
    		{
    			assert(mi==b[y]);
    			vis[y]=2;
    			q5.push(n5(y));
    		}
    		else//a+b
    		{
    			vis[z]=2;
    			if(f==1) assert(mi==a[z]+b[z]-a[xx]);
    			if(f==2) assert(mi==a[z]+b[z]-b[yy]);
    			assert(f>0);
    			if(f==1)
    				vis[xx]=0,q1.push(n1(xx)),q3.push(n3(xx));
    			if(f==2)
    				vis[yy]=1,q2.push(n2(yy)),q4.push(n4(yy));
    		}
    	}
    	//for(int i=1;i<=n;i++)
    	//	if(vis[i]==1) ans+=a[i];
    	//	else if(vis[i]==2) ans+=a[i]+b[i];
    	printf("%lld
    ",ans);
    	for(int i=1;i<=n;i++)
    		printf("%lld",vis[i]);
    }
    

    [NOI2019] 序列

    题目描述

    点此看题

    解法

    首先建出费用流模型(说实话有点难建),至少 (L) 个下标相同等价于至多 (k-L) 个下标不同,我们把选数看成一对一对地选,那么就等价于有 (k-L) 次机会不选下标相同的数,所以可以得到下图:

    不难发现对上图跑最大费用最大流就能得到答案,但是 (nleq 2cdot 10^5) 显然是跑不动网络流的。

    可以考虑模拟费用流,就是把费用流手玩出来嘛。那就要仔细研究研究这个图是怎么跑费用流的,首先如果红边有空余的流量,那么可以直接给红边流一点流量,因为经由他来流一定是最优的,这就相当于选一对未匹配 (A_i+B_j) 的最大。

    我们还可以直接流一点黑边,这就相当于选一对未匹配的 (A_i+B_i) 最大。

    最后一种方法就是使用费用流的返回边了,可以回撤某个 (A_i)(设其原来匹配的是 (B_k))到红边的流量,让他和 (B_i) 匹配,但是为了红边的流量平衡我们还需要补一个未匹配 (A_j) 的最大值上去,相当于我们匹配 (A_j)(B_k)

    还可以回撤某个 (B_i),这个和回撤 (A_i) 的方法是一样的。

    思路大概就是这样,最后讲一下实现的细节(这部分一定要认真看,不然会卡很久):

    • 我们要维护 (5) 个堆,分别是:未匹配 (A) 的最大值 (h1)(B) 匹配但 (A) 未匹配的 (A) 的最大值 (f1);未匹配 (B) 的最大值 (h2)(A) 匹配但是 (B) 未匹配的 (B) 的最大值 (f2)(A,B) 都未匹配的 (A+B) 最大值 (h3)

    • 模拟的时候就一点一点的加入流量,(4) 种流法混在一起写就行了。写的时候会涉及到很繁琐的出堆入堆,方便的写法是维护一个标记数组 (s),我们不急着弹堆,而是在取出堆顶的时候看他合不合法即可,不合法就弹出去。

    • 注意如果有下标相同的情况,就算是任意选择的,也不需要用红边了,一定要注意。

    虽然没有注释,但是代码还是很好看的

    #include <cstdio>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define ll long long
    const int M = 200005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,k,l,now,a[M],b[M],s[M];ll ans;
    struct n1
    {
    	int x;
    	n1(int X=0) : x(X) {}
    	bool operator < (const n1 &r) const
    	{
    		return a[x]<a[r.x];
    	}
    };priority_queue<n1> f1,h1;
    struct n2
    {
    	int x;
    	n2(int X=0) : x(X) {}
    	bool operator < (const n2 &r) const
    	{
    		return b[x]<b[r.x];
    	}
    };priority_queue<n2> f2,h2;
    struct n3
    {
    	int x;
    	n3(int X=0) : x(X) {}
    	bool operator < (const n3 &r) const
    	{
    		return a[x]+b[x]<a[r.x]+b[r.x];
    	}
    };priority_queue<n3> h3;
    void fuck()
    {
    	while(!h1.empty()) h1.pop();
    	while(!f1.empty()) f1.pop();
    	while(!h2.empty()) h2.pop();
    	while(!f2.empty()) f2.pop();
    	while(!h3.empty()) h3.pop();	
    	for(int i=1;i<=n;i++)
    	{
    		s[i]=0;
    		h1.push(n1(i));
    		h2.push(n2(i));
    		h3.push(n3(i));
    	}
    	while(k--)
    	{
    		while(!h1.empty() && (s[h1.top().x]&1)) h1.pop();
    		while(!f1.empty() && (s[f1.top().x]^2)) f1.pop();
    		while(!h2.empty() && (s[h2.top().x]&2)) h2.pop();
    		while(!f2.empty() && (s[f2.top().x]^1)) f2.pop();
    		while(!h3.empty() && s[h3.top().x]) h3.pop();
    		if(now)
    		{
    			now--;
    			int x=h1.top().x,y=h2.top().x;
    			ans+=a[x]+b[y];
    			s[x]|=1;s[y]|=2;
    			if(s[x]^3) f2.push(n2(x));
    			if(s[y]^3) f1.push(n1(y));
    			if(x==y) now++;
    			else
    			{
    				if(s[x]==3) now++;
    				if(s[y]==3) now++;
    			}
    			continue;
    		}
    		int v1=0,v2=0,v3=0,c1=0,c2=0;
    		if(!f2.empty())
    		{
    			v1=a[h1.top().x]+b[f2.top().x];
    			c1=s[h1.top().x]==2?1:0;
    		}
    		if(!f1.empty())
    		{
    			v2=a[f1.top().x]+b[h2.top().x];
    			c2=s[h2.top().x]==1?1:0;
    		}
    		if(!h3.empty())
    			v3=a[h3.top().x]+b[h3.top().x];
    		int mx=max(v1,max(v2,v3));ans+=mx;
    		if(v1==mx && (v1>v2 || (v1==v2 && c1>=c2)))
    		{
    			int x=h1.top().x,y=f2.top().x;
    			s[x]|=1;s[y]|=2;
    			if(s[x]^3) f2.push(n2(x));
    			else now++;
    		}
    		else if(v2==mx)
    		{
    			int x=f1.top().x,y=h2.top().x;
    			s[x]|=1;s[y]|=2;
    			if(s[y]^3) f1.push(n1(y));
    			else now++;
    		}
    		else s[h3.top().x]=3;
    	}
    }
    signed main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();k=read();l=read();
    		for(int i=1;i<=n;i++)
    			a[i]=read();
    		for(int i=1;i<=n;i++)
    			b[i]=read();
    		now=k-l;ans=0;
    		fuck();
    		printf("%lld
    ",ans);
    	}
    }
    

    楼房搭建

    题目描述

    这道题是校内模拟赛的题,没有 ( t source)

    (n) 个的楼房,初始时每个位置高度都是 (0),每次操作可以让相邻的两个楼房高度 (+1)(+2)(可以是左边加 (1),也可是是右边加 (1)),问最少需要操作多少次,使得操作后第 (i) 个楼房的高度不小于 (h_i)

    (1leq n,h_ileq 10^6)

    解法

    本题可以用单调队列优化 (dp) 做到 (O(ncdot h_i)),但是无法优化,这里就不展开讲了。

    先想一想我们是怎么乱贪心的,目的显然是让多加的高度最小,一种看起来比较合理的贪心是:当处理到建筑 (i) 的时候我们疯狂放 (2),然后建筑 (i+1) 就对应的放 (1)

    问题也是显然的,如果出现下图的情况就会 ( t Wa) 掉:

    最右边那个楼房就会浪费很多,我们本应该对第一个楼房实行 <1,2> 操作来削减第二个楼房留给后面的高度,但是我们无脑做 <2,1> 导致了错误,解决方法是在保证第一个楼房高度不变的情况下,我们尽可能升高第二个楼房

    可以用反悔贪心来实现它,具体地,我们用两个 <1,2> 操作来反悔一个 <2,1> 操作,这样第二个楼房就会升高 (3) 的高度。

    但是这样还是会出问题,你怎么知道什么时候反悔?如果我们无脑反悔的话可能出现第三个楼房极高,我们就不需要第二个楼房反悔得这么高的情况,那么我们现在从第三楼房的视角去看我们的反悔操作,现在保证第二个楼房的高度不变,那么可以用 <1,2>and<2,1> 或者是三个 <1,2> 把第三个楼房升高 (3) 或者 (6)也就是说反悔上一个建筑的反悔操作可以让这个楼房升高 (3) 或者 (6)

    有了这两个理论之后我们就可以无脑做了,相当于是每次让当前建筑尽量高,如果这样不合适也没关系,因为相信后面能够反悔过来,那么 (O(n)) 扫一遍就可以解决问题。

    实现的时候,"反悔反悔操作"和反悔操作放在一个变量存着即可,(+6) 可以看做两个 (+3)

    下面我嫖了 oneindark 巨佬的代码:

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    using namespace std;
    inline int readint(){
    	int a = 0; char c = getchar(), f = 1;
    	for(; c<'0'||c>'9'; c=getchar())
    		if(c == '-') f = -f;
    	for(; '0'<=c&&c<='9'; c=getchar())
    		a = (a<<3)+(a<<1)+(c^48);
    	return a*f;
    }
    int main(){
    	int n = readint();
    	long long ans = 0;
    	int v = 0; // already built
    	int chance = 0; // how many repent is allowed
    	for(int i=1; i<=n; ++i){
    		int h = readint(); ans += h;
    		h -= v, v = 0; // what's to do
    		if(h <= 0){ // finished
    			chance = 0; ans += (-h);
    			continue; // waste -h
    		}
    		int x = min(h/3,chance); // how many +3 is applied
    		int y = (h-3*x)>>1; // how many <2,1> is applied
    		v = (((h-3*x)&1)<<1)+y; // if <1,2> is applied
    		chance = (x<<1)+y; // +3 here can be twice +3 there
    	}
    	ans += v; // build on virtual n+1
    	printf("%lld
    ",ans/3);
    	return 0;
    }
    

    CF335F Buy One, Get One Free

    这题我也没搞懂,别看我写的东西!

    题目描述

    点此看题

    还是难啊,我本来以为我能直接切,但 (3000) 分的题怎么会那么容易!

    错解1

    首先考虑如果免费得到的是一个价格不严格小于的馅饼,那么就是一道水题,直接从大到小贪心即可。

    这道题直接贪心不行,就反悔啊。我们考虑把所有免费的馅饼放进单调栈里面,对于每个馅饼带走一个严格比他小的,如果带不走那么就考虑反悔,买栈顶那个免费的馅饼,就能多带走当前的两个馅饼,判断条件是 (free<2cdot a_i) 时反悔。

    样例随便过,但是一交上去就 ( t Wa) 穿了。

    错解2

    认真分析了一波,到处去翻题解。我发现不能带走严格比他小的馅饼啊,因为原本的贪心就不是这样设计的,原本的贪心是带走相邻的一个,不能这么乱做的。

    我写不动,脑子炸了,留坑待填。

    #include <cstdio>
    #include <algorithm>
    #include <queue>
    using namespace std;
    #define int long long
    const int M = 500005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,a[M],b[M],c[M],tmp[M];
    priority_queue<int,vector<int>,greater<int> > q;
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),ans+=a[i];
    	sort(a+1,a+1+n);
    	for(int i=1,j;i<=n;i=j)
    	{
    		j=i;b[++m]=a[i];
    		for(;a[i]==a[j];j++);
    		c[m]=j-i;
    	}
    	for(int i=m,sc=0;i>=1;i--)
    	{
    		int num=q.size(),tp=0;
    		int t=min(sc-2*num,c[i]),p=min(c[i],sc)-t;
    		for(int j=1;j<=t;j++)
    			tmp[++tp]=b[i];//dirctly greedy
    		for(int j=1;j<=p;j+=2)
    		{
    			int k=q.top();q.pop();
    			if(k<b[i])//regret previous REGRET
    			{
    				tmp[++tp]=b[i];
    				if(j<p) tmp[++tp]=b[i];
    			}
    			else//regret
    			{
    				tmp[++tp]=k;
    				if(j<p && 2*b[i]>k)
    					tmp[++tp]=2*b[i]-k; 
    			}
    		}
    		for(int j=1;j<=tp;j++)
    			q.push(tmp[j]);
    		sc+=c[i];
    	}
    	while(!q.empty()) ans-=q.top(),q.pop();
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    状压DP之排列perm
    CodeForces 578F Mirror Box
    Berlekamp-Massey算法
    图解git操作
    yapi安装
    springcloud gateway
    springcloud alibaba
    反射和内置方法
    绑定方法与非绑定方法
    多态性和鸭子类型
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14604078.html
Copyright © 2020-2023  润新知