1. 程式人生 > 實用技巧 >[線段樹]CF674 G - Choosing Ads

[線段樹]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
*/