P8365 [LNOI2022] 吃
给定 \(n\) 个二元组 \((a_i, b_i)\),\(s\) 初始为 \(1\)。每次从 \(n\) 个二元组中任选一个进行 \(\times a_i\) 或 \(+ b_i\),每个二元组只能选一次。求 \(s\) 的最大值对 \(10^9 + 7\) 取模的值。
思路
我们注意到取模断送了直接 DP 答案的解法,这说明在搬出算法之前多少要进行一些贪心分析。
首先我们发现,存在一种最优方案,使得先加后乘,不然把加的那个挪到乘前面显然更优。
考虑一下极端情况,\(a = 1\)。此时肯定选 \(+ b\),那由上面的结论,肯定先把这些加上。
考虑 \(a > 1\) 的情况,那么一次乘足以让上次加的 \(b\) 翻倍。因此,如果我们在两个 \(a > 1\) 的二元组上选择了做加法,只需要选择 \(b\) 较小的换成乘法,一定更优。这说明我们最多只需要选择一个 \(a > 1\) 的进行加法即可。
接下来我们推推式子就搞定了。
代码
#include <bits/stdc++.h>
#define int __int128
#define Misaka namespace
#define Network std
using Misaka Network;
const int N = 5e5 + 7, MOD = 1e9 + 7;
int n, K = 1;
pair<int, int> a[N];
void read(__int128 &x){
long long c; cin >> c; x = c;
}
void op(__int128 x){
long long res = x; cout << res << "\n";
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0);
read(n);
for(int i = 1; i <= n; i ++) read(a[i].first);
for(int i = 1; i <= n; i ++) read(a[i].second);
int ans = 1;
for(int i = 1; i <= n; i ++){
if(a[i].first == 1) ans += a[i].second;
(K *= a[i].first) %= MOD;
}
int idx = -1;
for(int i = 1; i <= n; i ++){
if(a[i].first == 1) continue;
if(idx == -1) idx = i;
else if(a[i].first * (ans + a[idx].second) < a[idx].first * (ans + a[i].second)) idx = i;
}
if(idx != -1 && ans + a[idx].second < a[idx].first * ans) op(K * ans % MOD);
else{
int sum = 1, mul = 1;
for(int i = 1; i <= n; i ++){
if(i == idx) (sum += a[i].second) %= MOD;
else if(a[i].first == 1) (sum += a[i].second) %= MOD;
else (mul *= a[i].first) %= MOD;
}
op(sum * mul % MOD);
}
return 0;
}

浙公网安备 33010602011771号