[CF643G]Choosing Ads

做题时间:2022.9.27

\(【题目描述】\)

给定一个长为 \(n(n\leq 1.5\times 10^5)\) 的序列和一个整数 \(p(20\leq p\leq 100)\) ,有 \(m(m\leq 1.5\times 10^5)\) 个操作操作要么是区间赋值,要么是对区间询问其中出现次数至少为 \(p\%\) 的数。

输出答案时可以包含错误的答案或是重复输出,但正确答案一定要在其中,且输出数的个数不能超过 \(\lfloor 100/p \rfloor\) 个。

\(【输入格式】\)

第一行三个整数 \(n,m,p\)

接下来 \(m\) 行,每行两或三个整数 \(opt,l,r,(id)\) ,若 \(opt=1\) 表示当前操作为区间赋值,若 \(opt=2\) 则表示当前操作为区间询问

\(【输出格式】\)

对于每个询问操作,首先输出数的个数 \(k_i\) 接下来输出 \(k_i\) 个数表示答案

\(【考点】\)

线段树、摩尔投票法

\(【做法】\)

NOI2022 D1T1加强版。

\(s=\lfloor 100/p \rfloor\) 首先答案肯定不会超过 \(s\),因此可以对于线段树上每一个点维护出现次数最多的前 \(s\) 个数及其出现次数。合并的时候,对于两个儿子 \(l_i,r_i\) ,便利一遍 \(r_i\),若当前的数字在 \(l_i\) 中出现过,则直接加进去;否则,若当前 \(l_i\) 中的数不超过 \(s\) ,也直接加进去;否则,采用摩尔投票法的对拼消耗,若当前 \(l_i\) 中出现次数最少的数的出现次数大于当前的 \(cnt_{r}\) ,那就将 \(l_i\) 中所有数的出现次数减去 \(cnt_r\),否则,剔除一个出现次数小于 \(cnt_r\) 的数,并同时将 \(l_i\) 中所有数的出现次数减去它。

\(【代码】\)

#include<cstdio>
#include<iomanip>
#define ls(k) k<<1
#define rs(k) k<<1|1
using namespace std;
const int N=2e5+50;
int a[N],n,m,p;
struct T{
	int num[6],cnt[6];
	int tot,tag;
	T(){
		for(int i=1;i<=5;i++) num[i]=cnt[i]=0;
		tot=tag=0;
	}
}tree[N<<2];
inline T Merge(T ls,T rs)
{
	T ans;
	ans.tot=ls.tot;
	for(int i=1;i<=ls.tot;i++){
		ans.num[i]=ls.num[i];
		ans.cnt[i]=ls.cnt[i];
	}
	for(int i=1;i<=rs.tot;i++){
		bool q=true;
		for(int j=1;j<=ans.tot;j++){
			if(rs.num[i]==ans.num[j]){//查找是否有相等的数
				ans.cnt[j]+=rs.cnt[i];
				q=false;
				break;
			}
		}
		if(q){
			if(ans.tot<100/p){//若l中的数不足100/p
				ans.num[++ans.tot]=rs.num[i];
				ans.cnt[ans.tot]=rs.cnt[i];
				continue;
			}
			int pos=1;
			for(int j=2;j<=ans.tot;j++){//查找l中最小的数
				if(ans.cnt[j]<ans.cnt[pos]) pos=j;
			}
			if(ans.cnt[pos]>rs.cnt[i]){//若其大于当前的r
				for(int j=1;j<=ans.tot;j++) ans.cnt[j]-=rs.cnt[i];
			}
			else{
				int red=ans.cnt[pos];
				ans.num[pos]=rs.num[i],ans.cnt[pos]=rs.cnt[i];
				for(int j=1;j<=ans.tot;j++) ans.cnt[j]-=red;
			}
					
		}
	}
	return ans;
}
inline void Build(int k,int l,int r)
{
	if(l==r){
		tree[k].tot=1;
		tree[k].num[1]=a[l],tree[k].cnt[1]=1;
		return ;
	}
	int mid=(l+r)>>1;
	Build(ls(k),l,mid),Build(rs(k),mid+1,r);
	tree[k]=Merge(tree[ls(k)],tree[rs(k)]); 
}
void Change(int k,int l,int r,int v)
{
	tree[k].tot=1;
	tree[k].num[1]=v,tree[k].cnt[1]=r-l+1;
	tree[k].tag=v;
}
inline void Pushdown(int k,int l,int r)
{
	if(!tree[k].tag) return ;
	int mid=(l+r)>>1;
	Change(ls(k),l,mid,tree[k].tag),Change(rs(k),mid+1,r,tree[k].tag);
	tree[k].tag=0;
}
inline void Modify(int k,int l,int r,int x,int y,int p)
{
	if(r<x||l>y) return ;
	if(x<=l&&r<=y) return Change(k,l,r,p);
	Pushdown(k,l,r);
	int mid=(l+r)>>1;
	if(x<=mid) Modify(ls(k),l,mid,x,y,p);
	if(y>mid) Modify(rs(k),mid+1,r,x,y,p);
	tree[k]=Merge(tree[ls(k)],tree[rs(k)]);
}
inline T Query(int k,int l,int r,int x,int y)
{
	if(r<x||l>y){T a;return a;}
	if(x<=l&&r<=y) return tree[k];
	Pushdown(k,l,r);
	int mid=(l+r)>>1;
	T ans;
	if(x<=mid) ans=Query(ls(k),l,mid,x,y);
	if(y>mid) ans=Merge(ans,Query(rs(k),mid+1,r,x,y));
	return ans;
}
int main()
{
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	Build(1,1,n);
	
	
	for(int i=1;i<=m;i++){
		int opt,l,r,id;
		scanf("%d%d%d",&opt,&l,&r);
		if(opt==1){
			scanf("%d",&id);
			Modify(1,1,n,l,r,id);
		}
		else{
			T ans=Query(1,1,n,l,r);
			printf("%d ",ans.tot);
			for(int i=1;i<=ans.tot;i++) printf("%d ",ans.num[i]);
			printf("\n");
		}
	}
	return 0;
}
posted @ 2022-09-29 19:07  lxzy  阅读(29)  评论(0)    收藏  举报