1918. 警钟长鸣

首先注意在题给条件下,第 i 秒的 x_i, y_i 的取值变化最多有 O(n) 种,即总共有 O(n) 种不同的不悦值。例如,对 [1,100] 区间并上 [10,1000] 区间,能获得不同取值的子区间只有 [1,9],[10,100],[101,1000],这三个区间内的每秒带来的不悦值必然是相等的。因为要输出最早,所以只需要考虑每个区间的左端点即可。

不妨离散化,将取值范围为 [1,10^9] 的区间范围压缩至 [1,n] 的范围。即将原本的数值 val 降低为它的排序排名 rank_{val}。这样就不会 MLE 了。具体实现时,可以通过 sort+unique / map 等方法离散化。也可以直接开 set/map 离散下标的数据结构。

现在考虑下一个子问题,即计算不悦值。我们发现 (x+y)!(x+1)(y+1) 非常大,远超 long long,且事实上 n! 也超过了 double 乃至 long double 的最大值,所以直接计算肯定是会溢出的。并且也不能通过增设模数即计算 (x+y)!(x+1)(y+1)\bmod p,因为模意义下无法比较大小。如果开高精度,多次计算有 TLE 和 MLE 的风险(显然,设计算 t 位高精度,单次准确计算加减的复杂度至少是 O(t),准确计算乘法的复杂度至少是 O(t\sqrt t)(FFT 丢精度,不保证 100% 准确),比较大小复杂度也是 O(t))。

注意到要比较 (x_1+y_1)!(x_1+1)(y_1+1)(x_2+y_2)!(x_2+1)(y_2+1),即两边取对数,比较 \ln(x_1+y_1)!+\ln(x_1+1)+\ln(y_1+1)\ln(x_2+y_2)!+\ln(x_2+1)+\ln(y_2+1)。显然取对数后数量级是不会溢出的,虽然损失了准确值(但是答案也没要求算准确值),但仍可以比较大小。显然 x+y\le n,故可以 O(n) 预处理出所有的阶乘对数 \ln n!=\ln(n-1)!+\ln n,然后就可以方便比较大小了。值得注意 \ln 函数计算是 O(1) 的。

总复杂度为 O(n\log n)(即离散化的复杂度)。

#include <bits/stdc++.h> using namespace std; using ll = long long; using db = double; const ll mn = 1e5 + 10; ll n, ans; db f[mn], mx = -1; map<ll, ll> s[3]; db g(ll x, ll y) { return log(x + 1) + log(y + 1) + f[x + y]; } signed main() { ios::sync_with_stdio(false), cin.tie(0); cin >> n; f[0] = 0; // ln(0!)=0 for (ll i = 1; i <= n; ++i) { f[i] = f[i - 1] + log(i); // ln(i!) } for (ll l, r, c; n; --n) { cin >> l >> r >> c; s[c][l]++; s[c][r + 1]--; s[3 - c][l] += 0, s[3 - c][r + 1] += 0; //使得s[1],s[2]拥有的下标一致 } s[1][0] += 0, s[2][0] += 0; auto i = s[1].begin(), j = i; for (++i; i != s[1].end(); ++i, ++j) { ll i2 = i->first, j2 = j->first; s[1][i2] += s[1][j2], s[2][i2] += s[2][j2]; ll x = s[1][i2], y = s[2][i2]; db res = g(x, y); if (res > mx + 1e-8) { mx = res; ans = i2; } } cout << ans; return 0; }