题解:P13100 [FJCPC 2025] 众数
这题题面有点难懂,是我菜了。
题意
一个
:::info[注解]{open}
①〔前缀〕集合
②〔非空子集〕在其中选任意至少一个元素的集和。
③〔
④〔
⑤〔众数〕所有数中出现次数最多的数,注意众数可以有多个,但题目要求的是最大众数。 :::
示例
样例 ,所以我们手玩样例
input:
5
1 2 3 4 5
output:
2 4 4 5 6
-
$\begin{cases} S_1=\{1\} & f(S_1)=2\\ \end{cases}$,最大众数为 $2$。 -
$\begin{cases} S_1=\{1\} & f(S_1)=2 \\ S_2=\{1,2\} & f(S_2)=3\\ S_3=\{2\} & f(S_3)=4 \\ \end{cases}$,最大众数为 $4$。 -
$\begin{cases} S_1=\{1\} & f(S_1)=2 \\ S_2=\{1,2\} & f(S_2)=3 \\ S_3=\{1,3\} & f(S_3)=4 \\ S_4=\{1,2,3\} & f(S_4)=4\\ S_5=\{2\} & f(S_5)=4 \\ S_6=\{2,3\} & f(S_6)=5 \\ S_7=\{3\} & f(S_7)=6 \\ \end{cases}$,最大众数为 $4$。 -
-
思路
因为
可以证明当有中间数存在时,最大众数就是整个前缀的最大值加最小值的和。
:::info[证明:存在中间数时,众数为最大值加最小值]
令当前前缀为:
当
接下来我们分析没有中间数的情况。
令最大值为
- 所有数都是最大值,因此所有子集中
\min(S)=\max(S)=a_x ,结果为2 \times a_x 。 - 所有数都是最小值,因此所有子集中
\min(S)=\max(S)=a_y ,结果为2 \times a_y 。 - 既有最大值也有最小值,因此所有子集中
\min(S)=a_y,\max(S)=a_x ,结果为a_x+a_y 。
众数是这三种集合数量最多的,于是我们分析三种集合分别的数量。
- 所有数都是最大值的集合,次数为
2^{k-b}-1 。 - 所有数都是最小值的集合,次数为
2^b-1 。 - 既有最大值也有最小值的集合,次数为
2^k-2^{k-b}-2^b+1 (空集被减了两次,需加回一次)。
可以证明,仅当
:::info[证明:没有中间数,最小值仅出现
此时,
当
结论
没有中间数且最小值仅出现一次,答案为
代码就很好写了。
代码
#include<iostream>
using namespace std;
const int inf=0x7fffffff;
int r(){//快读
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
int T=r();
while(T--){
int n=r();
int x=-inf,y=inf;//max和min
int cntx=0,cnty=0;//max和min分别的次数
for(int i=1;i<=n;i++){
int a=r();
if(x==a)cntx++;
else if(x<a)x=a,cntx=1;
if(y==a)cnty++;
else if(y>a)y=a,cnty=1;
if(cnty==1&&cntx+cnty==i)printf("%d ",2*x);//最小值仅出现一次,且没有中间数
else printf("%d ",x+y);//其余情况
}
printf("\n");
}
return 0;
}