P4105 南园满地堆轻絮 Sol

先放结论:答案是 \(\left\lfloor\dfrac{\max\{a_i-a_j\}(i < j,\space a_i\ge a_j)}{2}\right\rfloor\)

虽然说考场上贪心基本猜个结论就走人,不用证明,但是平时还是要会证明贪心正确性的。

\(O(n \log n)\) 做法:二分 + 贪心

首先这道题看到最大值最小可以想到二分。
那么想到二分答案,即这个最小差值。
这个时候可以将问题简化为:存在一个答案 \(ans\),将序列所有元素变为 \(a_i ± ans\),使得序列单调不减。

所以转化为判定问题,想到贪心策略。

check 函数中,记录扫到当前的 \(a_i\)\(\max\{a_j-ans\}\space(j<i)\),如果当前 \(a_i+ans<\max\) 那么一定无解。

证明挺显然的。

由于这里 \(ans\) 原来是差值的 \(\max\),即 \(±[0,ans]\) 都是可以的。上界为 \(a_i+ans\),下界为 \(a_i-ans\)。此时前面的下界大于此时的上界,可以 break。


\(O(n)\) 做法:贪心

贪心策略:找最大逆序对差值。

  1. 为什么是逆序对?

想一下 \(b\) 的变化图像一定有横坐标和纵坐标正相关。那么此时最劣情况必然是 \(a\) 的变化负相关。那么这差不多就是逆序对的定义。

  1. 为什么是最大逆序对?

最大逆序对的波动是最大的,相应地,需要满足的 \(ans\) 也是最大的。此时必然取中点答案最小,文章开头结论正确。

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 5e6 + 10;
int n, sa, sb, sc, sd, MOD, Max, res, a[N];

inline int f(int x) {
  int fi = sa * x % MOD * x % MOD * x % MOD;
  int se = sb * x % MOD * x % MOD;
  int th = sc * x % MOD;
  return (fi + se + th + sd) % MOD;
}

signed main() {
  cin >> n >> sa >> sb >> sc >> sd >> a[1] >> MOD;
  Max = a[1];
  for (int i = 2; i <= n; ++i) {
    a[i] = (f(a[i - 1]) + f(a[i - 2])) % MOD;
    if (a[i] <= Max) res = max(res, Max - a[i]);
    Max = max(Max, a[i]);
  }
  cout << (res + 1) / 2 << endl;
  return 0;
}
posted @ 2022-07-18 17:03  MistZero  阅读(18)  评论(0编辑  收藏  举报