Codeforces Round #657 (Div. 2).C
-
题意
- 给你 \(m\) 种花,在收到第 \(i\) 种类型的花后,幸福度会增加 \(a_i\),在收到每一朵连续的这种类型的花后,幸福度会增加 \(b_i\) ,也就是说如果收到 \(x_i\) 种类型为 \(i\) 的花,幸福度就会增加 \(a_i + (x_i-1)*b_i\) ,现在你可以得到 \(n\) 朵花,你需要求出能达到的多大的幸福度是多少。
-
解析
-
因为 \(n \leq 1e9\) ,所以 \(dp\) 肯定是行不通的,那就基本上只能考虑贪心。这个题刚开始还是很容易想到用贪心的方法,能想到的策略也有很多,我记得我听过一句话
想到一个贪心策略是很简单的,但是证明一个贪心策略是很难的
-
刚开始可能会想,\(a_i\) 最大的会不会一定要拿,或者 \(b_i\) 最大的要不要优先考虑
------ 100 2 2 1 1 100 ------ 2 3 8 1 7 1 6 2 ------
-
通过这两组样例就知道之前想的两个贪心策略都是错误的。
-
经过思考后就会发现,可能的情况是在太多,如果直接贪心完全无法保证结果最大。
-
那么这时候就要考虑先枚举一些东西。
-
我们先想想解的形式。
- 在 \(n \leq m\) 时,有可能会出现只选择,前 \(n\) 大的 \(a_i\) ,即按照 \(a\) 排序后,就取 \(a_1\)~ \(a_n\) 的和为一种可能的答案 ( 这是一种没有花选了多个的情况 )
- 接下来考虑某一种花选了多个的情况,其他某些花就一种或者一种也没有
- 只可能有一种花选了多个,即不可能有第二种花同样有多个
- 若第 \(i\) 种花选了多个,那么比 \(b_i\) 大的 \(a_j\) 一定都拿了,并且比 \(b_i\) 小的 \(a_j\) 均没拿
- 因为第 \(i\) 种花选了多个,那么无论 \(a_i\) 多大都要选。
-
这其实就是全部可能的解了,第一种情况很好写,现在直接想第二种情况。
-
虽然这个题是贪心,但不代表不能枚举,所以我们如果枚举第 \(i\) 种花选了多个,按照上述规则找到一个可行解,只要找到所有的取最大值即可。
-
那么现在选了第 \(i\) 种,该如何快速得到比 \(b_i\) 大的 \(a_j\) 的和呢?
-
我们将 \(a\) 先排序,同时也将 \(b\) 排序 ( 分开存且均从大到小 )
-
由于 \(b_i \ge b_{i+1}\) 所以所有比 \(b_{i+1}\) 大的 \(a_j\) 一定包含了所有比 \(b_i\) 大的,由于 \(a\) 也是有序的,那么只需要将在 \(b_i\) 与 \(b_{i+1}\) 范围内的 \(a\) 加进和即可 ( 记得统计 \(a_i\) 是否被包括进和里,也可通过 \(a_i\) 和 \(b_i\) 的大小关系判断,如果 \(a_i \ge b_i\) 那么被算进和了) ,还需要注意 只能选 n 个数!
-
-
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Maxn = 1e5+10;
const int Inf = 0x7f7f7f7f;
const int Mod = 1e9+7;
struct Flower1{
int a,pos;
}f1[Maxn];
struct Flower2{
int b,pos,a;
}f2[Maxn];
bool cmpa(Flower1 a,Flower1 b){
return a.a > b.a;
}
bool cmpb(Flower2 a,Flower2 b){
return a.b > b.b;
}
bool vis[Maxn];
int main(){
int T;
scanf("%d",&T);
while( T-- )
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d %d",&f1[i].a,&f2[i].b), f1[i].pos = f2[i].pos = i, f2[i].a = f1[i].a;
// 排序
sort(f1+1,f1+1+m,cmpa);
sort(f2+1,f2+1+m,cmpb);
ll ans = 0,Sum = 0;
// 处理第一个解的情况
if( n <= m )
for(int i=1;i<=n;i++) ans += f1[i].a;
for(int j=1,i=1;j<=m;j++)
{
// 将比 f2[j].b 大的 a 算进和
while( i <= n && i <= m && f1[i].a >= f2[j].b )
{
Sum += f1[i].a;
vis[f1[i].pos] = true;
i++;
}
// 只能选 n 个
if( i == n+1 ) break;
if( vis[f2[j].pos] ) // f2[j].a >= f2[j].b
ans = max(ans,Sum + 1ll*f2[j].b*(n-i+1));
else
ans = max(ans,Sum + 1ll*f2[j].a + 1ll*f2[j].b*(n-i));
}
printf("%lld\n",ans);
for(int i=1;i<=m;i++) vis[i] = false;
}
return 0;
}