一道思维题(有意思)

U431308 Maximum-Gap

给你一个序列 \(a\),求它排完序后的 \(\max(\lvert a_i-a_{i-1}\rvert)\),其实就是求一个序列排完序后的相邻元素的最大差值。
样例:
输入: \(n=4,a=[11,2,7,5]\)
输出: \(4\)
请你在不使用任何排序,值域很大的情况下以 \(O(n)\) 的复杂度解决这个问题。
有想法可以在评论区留言🤓️🤓️
大概在晚上公布一下原贴地址。
这题是最大间隙问题,出自codeforces的讨论 The smartest stupid problem that you might see today
这道题如果值域可以接受的话,就可以使用桶排,是个经典的 hard 面试题,好几个网站(比如 LeetCode)上都有,但这个没啥意思。CSacademy 上有原 Consecutive Max Difference

undate on 5.14
考虑找一个数 \(x\),使得 \(x\) 最大且 \(x\le ans\),不难发现 \(x=\frac{max-min}{n-1}\) 时满足条件,所以我们按值域,以 \(x\) 为块长分个块,然后将每个元素一次入块,更新块内的最大值最小值,答案就是两个邻接的块中,这个块的最小值减去上一个块的最大值,取 \(\max\) 即可。
这题还有个毒瘤题目 P4604 [WC2017] 挑战
可以有另一种做法,转进制后用基数排序,几乎是 \(O(n)\) 的,感觉非常牛逼。

const int N = 2e7 + 10;
int n;
long long a[N], t_max[N], t_min[N], maxn = 0, minn = 2e18;
unsigned long long seed;
bool vis[N];
signed main() {
    int t;
    cin >> t;
    cin >> seed;
    Seed(seed);
    while (t--) {
        ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
        cin >> n;
        memset(t_max, 0, sizeof(t_max));
        memset(t_min, 0x7f, sizeof(t_min));
        memset(vis, 0, sizeof(vis));
        maxn = 0, minn = 2e18;
        for (int i = 1; i <= n; ++i) a[i] = Rand(), maxn = max(maxn, a[i]), minn = min(minn, a[i]);
        long long x = (maxn - minn) / (n - 1) - 1;
        long long tot = 0;
        for (int i = 1; i <= n; ++i) {
            long long pos = (a[i] - minn - 1) / x + 1;
            tot = max(pos, tot);
            vis[pos] = 1;
            t_max[pos] = max(t_max[pos], a[i]);
            t_min[pos] = min(t_min[pos], a[i]);
        }
        long long last = t_min[1], ans = 0;
        for (int i = 1; i <= tot; ++i) {
            if (vis[i]) {
                ans = max(t_min[i] - last, ans);
                last = t_max[i];
            }
        }
        cout << ans << '\n';
    }
}
posted @ 2024-05-14 10:17  Ishar-zdl  阅读(61)  评论(1编辑  收藏  举报