[线段树]CF674 G - Choosing Ads

如果 \(p>50\),那么这个问题就是一个经典的众数问题,有一个 \(O(n)\) 的做法:维护一个二元组 \((w,c)\),遇到一个数 \(x\),若 \(x=w\),++\(c\),否则 --\(c\)。当 \(c\) 恰好减到 \(0\) 时,二元组变为 \((x,1)\) 然后接着做下去,最后二元组里的数 \(w\) 就是众数。因为一个众数抵消一个其他的数,抵消完后,最后一定还剩下至少一个众数。

我们想办法把这个算法拓展到 \(p\) 更小的情况,其实我们发现 \(p\) 的大小跟最多允许存在多少个众数成反比,即 \(\lfloor\frac{100}{p}\rfloor\)。换作这道题,就变成了求出现次数前这么多大的数。套用上面的算法试试:维护 \(\lfloor\frac{100}{p}\rfloor\) 个二元组 \((w_i,c_i)\),遇到一个数 \(x\),若存在 \(w_i=x\),++\(c_i\),否则将所有的 \(c_i\) 减一。当某个 \(c_i\) 恰好减到 \(0\) 时,将这个二元组替换为 \((x,1)\) 然后接着做下去,最后这若干个二元组中的 \(w_i\) 就是出现次数最多的那么多个数。(是不是跟上面的算法如出一辙?)

这题涉及区间操作,所以考虑用线段树来维护,每个结点挂几个二元组。现在的问题就变为怎么合并两个区间的二元组集合。枚举一个集合中的二元组 \((w,c)\),则:

  1. \(w\) 出现过,直接把 \(c\) 贡献上去即可。
  2. 否则若另一个集合的大小 \(< \lfloor\frac{100}{p}\rfloor\),则直接插入。
  3. 否则若另一个集合里 \(c\) 的最小值大于当前这个二元组的 \(c\) 值,则将集合内所有的 \(c\) 值减去当前二元组的 \(c\) 值。
  4. 否则将 \(c\) 值最小的二元组替换为当前二元组,并将集合内所有的 \(c\) 值减去被替换掉的那个二元组的 \(c\) 值。

复杂度 \(O(n\; \log n\; (\lfloor\frac{100}{p}\rfloor)^2)\),跑不满,可过。

code below:

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=1002020;
int n,p;
int wt[N],tag[N<<2],vis[20];
struct node{
	int w,c;
	node(int w1=0,int c1=0){w=w1;c=c1;};
};
vector<node> tr[N<<2],res,tmp;
vector<node> merge(int x,int y){
//	swap(x,y);
//	cout<<"merge:\n";
//	for(int i=0;i<tr[x].size();i++)cout<<tr[x][i].w<<" ";cout<<endl;
//	for(int i=0;i<tr[y].size();i++)cout<<tr[y][i].w<<" ";cout<<endl;
/*	res=tr[y];
	for(int i=0;i<tr[x].size();i++){
		bool fl=1; int mi=2e9;
		for(int j=0;j<res.size() && fl;j++){
			if(res[j].w==tr[x][i].w)
				res[j].c+=tr[x][i].c,fl=0;
			mi=min(mi,res[j].c);
		}
		if(!fl)continue;
		if(res.size()<100/p)res.push_back(tr[x][i]);
		else if(tr[x][i].c<mi){
//			cout<<"in1"<<endl;
			for(int i=0;i<res.size();i++)
				res[i].c-=tr[x][i].c;
		} else{
//			cout<<"in2:"<<mi<<endl;
			res.push_back(tr[x][i]);
			tmp.clear();
			for(int i=0;i<res.size();i++)
				if(res[i].c!=mi)tmp.push_back(res[i]);
			swap(res,tmp);
		}
	}*/
//	for(int i=0;i<res.size();i++)cout<<res[i].w<<" ";cout<<endl;
	res.clear(); tmp=tr[x];
	for(int i=0;i<tr[y].size();i++){
		int fl=0;
		for(int j=0;j<tmp.size();j++)
			if(tr[y][i].w==tmp[j].w){tmp[j].c+=tr[y][i].c;fl=1;break;}
		if(!fl)tmp.push_back(tr[y][i]);
	}
	for(int i=0;i<tmp.size();i++){
		if(res.size()<100/p)
			res.push_back(tmp[i]);
		else{
			int pos=0;
			for(int j=0;j<res.size();j++)
				if(res[j].c<res[pos].c)pos=j;
			if(res[pos].c>tmp[i].c){
				for(int j=0;j<res.size();j++)
					res[j].c-=tmp[i].c;
			} else{
				int d=res[pos].c; res[pos]=tmp[i];
				for(int j=0;j<res.size();j++)res[j].c-=d;
			}
		}
	}
	return res;
}
void build(int k,int l,int r){
	if(l==r)return tr[k].push_back(node(wt[l],1)),void();
	int mid=(l+r)>>1;
	build(k<<1,l,mid); build(k<<1|1,mid+1,r);
	tr[k]=merge(k<<1,k<<1|1);
}
void down(int k,int l,int r){
	if(tag[k]){
		int mid=(l+r)>>1;
		tr[k<<1].clear(); tr[k<<1|1].clear();
		tr[k<<1].push_back(node(tr[k][0].w,mid-l+1));
		tr[k<<1|1].push_back(node(tr[k][0].w,r-mid));
		tag[k<<1]=tag[k<<1|1]=1; tag[k]=0;
	}
}
void change(int k,int l,int r,int ql,int qr,int w){
	if(ql<=l && qr>=r){
		tr[k].clear(); tr[k].push_back(node(w,r-l+1)); tag[k]=1;
		return;
	} down(k,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid)change(k<<1,l,mid,ql,qr,w);
	if(qr>mid)change(k<<1|1,mid+1,r,ql,qr,w);
	tr[k]=merge(k<<1,k<<1|1); //updata(k);
}
void query(int k,int l,int r,int ql,int qr){
	if(ql<=l && qr>=r)
		return tr[0]=merge(0,k),void();
	down(k,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid)query(k<<1,l,mid,ql,qr);
	if(qr>mid)query(k<<1|1,mid+1,r,ql,qr);
}
int read(){
	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;
}
/*void print(int k,int l,int r){
	cout<<k<<" ["<<l<<","<<r<<"] : ";
	for(int i=0;i<tr[k].size();i++)cout<<tr[k][i].w<<"("<<tr[k][i].c<<") ";cout<<endl;
	if(l==r)return;
	int mid=(l+r)>>1; down(k,l,r);
	print(k<<1,l,mid); print(k<<1|1,mid+1,r);
}*/
int main()
{
	n=read(); int aq=read(); p=read();
	for(int i=1;i<=n;i++)wt[i]=read();
	build(1,1,n);
	while(aq--){
		int op=read(),l=read(),r=read();
		if(op==1)change(1,1,n,l,r,read());
		else{
			tr[0].clear();
			query(1,1,n,l,r);
			printf("%d ",tr[0].size());
			for(int i=0;i<tr[0].size();i++)
				printf("%d ",tr[0][i].w); putchar('\n');
		}
//		print(1,1,n);
	}
	return 0;
}
/*
10 5 50
5 1 3 2 4 1 1 4 2 1
1 5 7 3
1 7 8 6
1 8 9 5
1 3 5 5
2 7 10

5 1 5 5 5 3 6 5 5 1
*/
posted @ 2020-12-30 21:25  lindongli2004  阅读(60)  评论(0编辑  收藏  举报