传送门:CF
前题提要:SB的题目假暗示
首先读完题目,不难发现会有一个贪心结论.对于现阶段能打折显然我们直接就买,因为对于现阶段已经能打折的物品来说,无论我们之后买多少物品,这个物品的价格都是不动的.但是我们当前买这个物品又能为后面的物品提供基数.所以对于状态为打折的物品显然我们越早买越好.
很好,现在的问题就来到了,我们当前没有能打折的物品,我们应该怎么办呢.贪心的去想,我们显然应该去买打折要求最高的物品,因为对于那些物品来说,他们打折的要求最高,就最不可能满足打折条件.但是所有的物品对打折都有贡献的.所以我们应该拿那些越难打折的物品去贡献那些越简单打折的物品,这样才是最优的
然后现在是最后一个问题了.只要你读完了题目,你就会发现题目中特意告诉你了,你可以去买那些已经打折的物品(即使他的需要已经满了),然后拿那些物品去让后面的物品打折.这个其实是一个大坑,因为你经过分析之后其实会发现这种操作一定是不优的.也就是我们根本不会使用这种操作.但是为什么呢?这部分贪心证明全网详细解释的博客很少(这也是本篇博客诞生的原因).
对于我们之前讨论出来的结论来说,我们最终的序列一定是 1 , 1 , 1....1 , 2 , 2 , 2 , . . . 2 , 1 , 1..1 , 2 , 2... , 1 , 1 1,1,1....1,2,2,2,...2,1,1..1,2,2...,1,1 1,1,1....1,2,2,2,...2,1,1..1,2,2...,1,1这样的,也就是连续的2后面接着1然后继续接着2.并且那些2的打折要求是递减的(也就是b数组的大小是递减的).那么此时我们给这个序列的头部加上了一个 1 1 1,此时会导致什么影响呢.你会发现这个最多(当然也可能没影响,分析方式一样,故此处不考虑没影响的情况)只会对我们序列中最后一个2产生影响.我们来详细分析一下为什么是这样的,对于我们新增的1,我们序列中已经打折的1显然是不受影响的,现在只需要考虑2.
可以用反证法来证明不可能有两个2同时受影响.如果有两个2同时受影响,那么就意味着这个两个2都只需要前面再来一个物品就步入打折状态了,那么位置在前面的那个2显然可以换到后面那个的位置,此时我们发现第一个2就变成了打折状态,此时才是最优的.与我们之前的序列最优产生了矛盾.
所以假设我们此时会新增一个打折的,那么显然是最后的那个2进入了打折行列.并且有且只有这个2,简单来说,造成的影响就是将最后一个2加入了它后面1的行列.所以也就是相当于只影响了一个物品从非打折变成了打折而已.你算一下此时的花费,发现是一样的.所以并不会产生影响.
下面是具体的代码部分:
#include
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
inline void print(__int128 x){
if(x<0) {putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
#define maxn 1000000
#define int long long
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Node{
int a,b,id;
bool operator < (const Node &rhs) const {
return b<rhs.b;
}
}node[maxn];
signed main() {
int n=read();
for(int i=1;i<=n;i++) {
node[i].a=read();node[i].b=read();node[i].id=i;
}
sort(node+1,node+n+1);
int l=1,r=n;int ans=0;int sum=0;
while(l<=r) {
while(l<=r&&node[l].b<=sum) {
sum+=node[l].a;ans+=node[l].a;
l++;
}
if(l>r) break;
int temp=min(node[r].a,node[l].b-sum);
node[r].a-=temp;
sum+=temp;ans+=2*temp;
if(node[r].a==0) r--;
}
cout<<ans<<endl;
return 0;
}