题解 P7291 【人赢】

P7291 人赢

「EZEC-5」人赢 加强版

弱化版传送门

传送门

本来想去弱化版发题解的,但是题解区一下就满了...

原题被最后几个点卡着的 dalao 们可以去 这里 测试,这几组就是我造的 hack 数据。

显然 $n\leqslant 10^7$ 的数据范围要求了我们用 $\texttt{O}(n)$ 的算法。

思路

$\texttt{O}(n^2)$

我们显然可以暴力枚举 $x,y$ 求最大值。

$\texttt{O}(n \log n)$

我们可以将每个数按照值域从大到小排序,从前到后扫一遍,在路上求出原始编号的前缀最大值,直接求 $f$ 函数即可。

正确性证明:由于现在扫到的数一定比前面的数小,所以要使编号尽量大才能使 $f$ 函数最大。

代码很好写,就了,想看可以去其它题解或原题题解。

$\texttt{O}(n)$

方法 1

我们发现 $\texttt{O}(n \log n)$ 做法的扫一遍是 $\texttt{O}(n)$ 的,复杂度瓶颈在排序的 $\texttt{O}(n \log n)$,于是我们可以使用基数排序,期望复杂度 $\texttt{O}(n)$,但是常数巨大,无法通过本题。

方法2

在 $\texttt{O}(n \log n)$ 的做法中,我们为了保证正确性,我们给原序列排了序,现在我们要在 $\texttt{O}(n \log n)$ 的做法中找到一个正确的做法保证正确性。

我在这里先给出方法:先取出 $k_x\times x$ 的最大值,再从前后分别扫一遍,分别取前后缀的 $k_i\times i$ 的最大值,再路上求 $(i+x)\times \min(k_i,k_x)$ 的最大值即可。

正确性证明:我们假设最终的答案为 $(x,y)$ ,如果我们在这种方法下我们没法取到 $(x,y)$ 当且仅当存在 $x<z<y$ 满足:

$$\begin{cases}k_z\times z >k_x \times x\\k_z\times z >k_y \times y\end{cases}$$

我们分类讨论:

  • 1.当 $k_x>\min(k_x,k_y)$ 时,则将 $x$ 换为 $z$ 答案不会变劣,与最终的答案为 $(x,y)$ 矛盾。
  • 2.当 $k_z\leqslant\min(k_x,k_y)$ 时,则 $k_z\times z \leqslant k_y \times y$ ,与假设矛盾。

所以一定能取到最优解。

这种方法给出代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t,n,y;
long long now;
namespace IO{
    //read()
}using namespace IO;
ll a[10000010];
int x;
ll maxn;
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        now=max(now,a[i]*i);
        now=max(now,(i+x)*min(a[i],a[x]));
        if(a[i]*i>maxn) maxn=a[i]*i,x=i;
    }
    maxn=x=0;
    for(int i=n;i>=1;i--){
        now=max(now,(i+x)*min(a[i],a[x]));
        if(a[i]*i>maxn) maxn=a[i]*i,x=i;
    }
    printf("%lld",now);
    return 0;
}

卡了点常数,卡到了 $249ms$ 最优解。

这是一道思维题,对码量没什么要求。

那就再见了 $qwq$~

posted @ 2021-01-27 07:51  ffffyc  阅读(15)  评论(0)    收藏  举报  来源