题解 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$~

浙公网安备 33010602011771号