Loading

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;
}
posted @ 2026-08-01 10:41  Trent900  阅读(16)  评论(0)    收藏  举报