ZigZagKmp
Think twice, code once.

题意简述

给定长度为 \(n\) 的序列和整数 \(P\) ,有 \(m\) 次操作。操作格式如下:

  • 1 l r v \([l,r]\) 区间赋值为 \(v\)
  • 2 l r 查询 \([l,r]\) 中出现频率不少于 \(P\%\) 的数。
    询问2可以输出错误数字,也可以重复输出,但是总数不能超过 \(\lfloor \frac{100}{P} \rfloor\)

\(n,m\le 1.5\times 10^5,20\le P\le 100\)

算法分析

我的做法与题解中的主流做法似乎不同。

我们先考虑证明,如果能求出两个长度相等的区间内出现频率不小于 \(P\%\) 的数集合\(A,B\),那么这个集合的并中满足要求的数 \(x\in A\cup B\)

这个不难证明,如果一个数在两个区间内出现的频率都小于 \(P\%\) ,记分别出现 \(x,y\) 次,则 \(x<len\times P\%,y<len\times P\%\) 那么两边出现的次数和为 \(x+y\) ,一定小于 \(2\times len \times P\%\) ,因此这个数不符合要求,不一定要输出。

因此我们考虑维护每一个区间内出现次数最多的 \(K=\lceil\frac{100}{P}\rceil\) 个元素及其出现次数,合并直接把相同的数次数累加,得到至多 \(2K\) 个元素,取出现次数前 \(K\) 大的即可。

具体实现时,我们首先把 \(n\) 变成比 \(n\) 大的第一个 \(2^p\) 的数,这样我们建立的线段树就是一个满二叉树,并且每一次合并都是两个相等长度的区间的合并。

时间复杂度 \(O\left((n+m)K^2\log n\right)\) ,K含义见上文。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define maxn 300005
#define maxm 2000005
#define inf 0x3f3f3f3f
#define LL long long
#define mod 1000000007
#define local
void file(string s){freopen((s+".in").c_str(),"r",stdin);freopen((s+".out").c_str(),"w",stdout);}
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m,P;
int a[maxn];
struct nd{
	int id,vl;
	bool operator <(nd y)const{
		return vl>y.vl;
	}
};
nd tmp[20];int cnt;
#define lson ((x<<1))
#define rson ((x<<1)|1)
#define mid ((l+r)>>1)
struct node{
	nd nm[7];
	node operator +(node y)const{
		node z;
		cnt=0;
		for(int i=1;i<=P;++i)tmp[++cnt]=nm[i];
		for(int i=1;i<=P;++i){
			int flg=0;
			for(int j=1;j<=P;++j){
				if(y.nm[i].id==tmp[j].id){
					flg=j;break;
				}
			}//合并
			if(flg)tmp[flg].vl+=y.nm[i].vl;//处理相同元素
			else tmp[++cnt]=y.nm[i];//处理不同元素
		}
		sort(tmp+1,tmp+cnt+1);
		for(int i=1;i<=P;++i)z.nm[i]=tmp[i];//取前K大
		return z;
	}
}st[maxn<<2];
int laz[maxn<<2];
void pushup(int x){st[x]=st[lson]+st[rson];}
void build(int x,int l,int r){
	if(l==r){
		st[x].nm[1]=(nd){a[l],1};
		return;
	}
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(x);
}
void pushc(int x,int l,int r,int v){memset(st[x].nm,0,sizeof(st[x].nm));st[x].nm[1]=(nd){v,r-l+1};laz[x]=v;}
// 下推懒标记直接保留一个就行了
void pushdown(int x,int l,int r){
	if(laz[x]){
		pushc(lson,l,mid,laz[x]);
		pushc(rson,mid+1,r,laz[x]);
		laz[x]=0;
	}
}
void upd(int x,int l,int r,int L,int R,int v){
	if(l>R||r<L)return ;
	if(l>=L&&r<=R){
		pushc(x,l,r,v);
		return;
	}
	pushdown(x,l,r);
	upd(lson,l,mid,L,R,v);
	upd(rson,mid+1,r,L,R,v);
	pushup(x);
}
node query(int x,int l,int r,int L,int R){
	if(l>=L&&r<=R)return st[x];
	pushdown(x,l,r);
	if(L>mid)return query(rson,mid+1,r,L,R);
	if(R<=mid)return query(lson,l,mid,L,R);
	return query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
}
signed main(){
	read(n);read(m);read(P);
	P=100/P;++P;//这里直接+1,主要是后面输出个数处理方便
	for(int i=1;i<=n;++i)read(a[i]);
	
	int NN;for(NN=1;NN<n;NN<<=1);n=NN;//扩成2^p
	
	build(1,1,n);
	for(int i=1,op,l,r,v;i<=m;++i){
		read(op);read(l);read(r);
		if(op==1){
			read(v);upd(1,1,n,l,r,v);
		}
		else{
			printf("%d",P-1);
			node tt=query(1,1,n,l,r);
			for(int j=1;j<P;++j){
				printf(" %d",tt.nm[j].id?tt.nm[j].id:1);
			}
			puts("");
		}
	}
	return 0;
}

posted on 2021-02-04 17:07  ZigZagKmp  阅读(75)  评论(0编辑  收藏  举报