IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> [ZJOI2022]众数 -> 正文阅读

[数据结构与算法][ZJOI2022]众数

众数

题解

F u t a R i m e W o a w a S e t e \rm F\color{red}{utaRimeWoawaSete} FutaRimeWoawaSete说了说这道题,就去做了一下。说不定是今年ZJOI唯一一道签到题。

由于它要输出可能的最大值并将所有可能的最大值都输出,那么我们显然都要尽可能求出某个数可能的最多出现次数。
由于我们的 a i a_i ai?并不全相等,所以我们最后的可能成为众数的值一定是一个原先出现过的数,而且这个值必然是将原来一个区间 ( l , r ) (l,r) (l,r)中的数 x x x变到区间外的数 y y y而得到的。
如果只是某一个数变成的话,我们不如不变那一个数,将其它数变到它身上,这样反而能够得到更大的答案。

如果数 x x x出现的位置时 { p 0 = 0 , p 1 , p 2 , . . . , p k ? 1 , p k = n + 1 } \{p_0=0,p_1,p_2,...,p_{k-1},p_k=n+1\} {p0?=0,p1?,p2?,...,pk?1?,pk?=n+1},记区间 ( l , r ) (l,r) (l,r)中出现次数最多的数出现了 c ( l , r ) c(l,r) c(l,r)次,那么 x x x最后的答案便是 max ? 0 ? i ? j ? k ( k ? j + i + c ( p i , p j ) ) \max_{0\leqslant i\leqslant j\leqslant k}(k-j+i+c(p_i,p_j)) max0?i?j?k?(k?j+i+c(pi?,pj?))
一种可行的方法是枚举出现在 ( p i , p j ) (p_i,p_j) (pi?,pj?)中出现次数最多的数为 x x x
记前缀 i i i中出现了 p r e i pre_i prei? x x x,显然,我们可以改写一下上面的式子,
a n s x = k + max ? 0 ? i ? j ? k ( ( p r e p i + i ) ? ( p r e p j + j ) ) ans_x=k+\max_{0\leqslant i\leqslant j\leqslant k} \left((pre_{p_i}+i)-(pre_{p_j}+j)\right) ansx?=k+0?i?j?kmax?((prepi??+i)?(prepj??+j))这样不就只需要记录下一个前缀最小的 p r e p i + i pre_{p_i}+i prepi??+i,就行了吗?
于是我们就得到了一个 O ( n 2 ) O\left(n^2\right) O(n2)做法了吗,但显然是过不了 n ? 2 × 1 0 5 n\leqslant 2\times 10^5 n?2×105的数据的。

没事,我们继续想想上面方法的瓶颈主要是在什么地方。
显然这个方法我们对于每个颜色作为中间的数都要将整个序列扫一遍,而许多出现次数很少的颜色其实是完全没必要扫一遍的。
不如考虑根号分治,我们对出现次数很少的颜色想另外的做法。
由于这些颜色出现不超过 B B B次,不妨就枚举我们区间内出现最多的颜色出现 i ∈ [ 1 , B ] i\in[1,B] i[1,B]次时的答案。
显然,对于每个左端点,当右端点为 r r r时,最右边的左端点 l l l使得 ( l , r ) (l,r) (l,r)中有颜色出现 i i i次的这个 l l l显然是关于 r r r单增的。
这样我们就可以只需要知道在 [ 1 , l ] ∪ [ r , n ] [1,l]\cup[r,n] [1,l][r,n]中出现次数最多的颜色的出现次数就行。
显然,对于任意的一种颜色,我们让 a r a_r ar?刚好为这种颜色显然是最优的,那么我们需要知道的其实是 [ 1 , l ] ∩ [ r , n ] [1,l]\cap[r,n] [1,l][r,n]中出现了多少个颜色 a r a_r ar?即可。
这个由于我们的 r r r是不断右移的,所以我们也可以对颜色 a r a_r ar?设计一个指针,可以用前向星维护,让他跑到在 l l l右边的最后一个 a r a_r ar?处,这样就可以很简单的统计这东西了。
我们也就用 O ( n B ) O\left(nB\right) O(nB)的时间完成了出现次数少于 B B B的颜色的答案统计。

总时间复杂度 O ( n 2 B + n B ? n n ) O\left(\frac{n^2}{B}+nB\geqslant n\sqrt{n}\right) O(Bn2?+nB?nn ?),让 B B B n \sqrt{n} n ?时最优。

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 200005
#define MAXM (1<<18)+5
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int iv2=5e8+4;
const int lim=1000000;
const int n1=300;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int T,n,a[MAXN],b[MAXN],cnt[MAXN],maxx[MAXN];
int pre[MAXN],nxt[MAXN],head[MAXN],dp[MAXN];
int tot,buc[MAXN],pos[MAXN],id[MAXN],ans;
int main(){
	read(T);
	while(T--){
		read(n);for(int i=1;i<=n;i++)read(a[i]),b[++tot]=a[i];
		sort(b+1,b+tot+1);tot=unique(b+1,b+tot+1)-b-1;
		for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
		for(int i=1;i<=n;i++)cnt[a[i]]++,id[i]=cnt[a[i]];
		for(int i=1;i<=tot;i++)maxx[i]=cnt[i];
		for(int i=n;i>0;i--)nxt[i]=head[a[i]],pre[head[a[i]]]=i,head[a[i]]=i;
		for(int i=1;i<=tot;i++)if(cnt[i]>n1)
			for(int j=1,num=0;j<=n;j++){
				const int x=a[j];
				if(x==i)num++;
				else maxx[x]=max(dp[pre[j]]+num+cnt[x]-id[j]+1,maxx[x]),
					dp[j]=max(dp[pre[j]],id[j]-num),
					maxx[x]=max(maxx[x],dp[j]+cnt[i]);
			}
		for(int i=min(n1,n);i>0;i--){
			int num=0,l=1;
			for(int j=1;j<=tot;j++)buc[j]=0,pos[j]=head[j];
			for(int j=1;j<=n;j++){
				const int x=a[j];buc[x]++;if(buc[x]==i)num++;
				while(l<=j&&num>(buc[a[l]]==i)){
					if(buc[a[l]]==i)num--;
					buc[a[l]]--;l++;
				}
				while(nxt[pos[x]]&&nxt[pos[x]]<l)pos[x]=nxt[pos[x]];
				int tmp=cnt[x]-id[j]+1+id[pos[x]]-(pos[x]>=l);
				if(num)maxx[x]=max(maxx[x],tmp+i);
			}
			for(int j=1;j<=tot;j++){
				while(nxt[pos[j]]&&nxt[pos[j]]<l)pos[j]=nxt[pos[j]];
				if(num)maxx[j]=max(maxx[j],id[pos[j]]+i-(pos[j]>=l));
			}
		}
		int mx=0;
		for(int i=1;i<=tot;i++)mx=max(mx,maxx[i]);
		printf("%d\n",mx);
		for(int i=1;i<=tot;i++)if(maxx[i]==mx)printf("%d\n",b[i]);
		for(int i=1;i<=tot;i++)head[i]=cnt[i]=0;tot=0;
		for(int i=1;i<=n;i++)pre[i]=nxt[i]=id[i]=0;
	}
	return 0;
}

谢谢!!!

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 12:08:32  更:2022-05-10 12:11:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:26:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码