如果 \(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)\),则:
- 若 \(w\) 出现过,直接把 \(c\) 贡献上去即可。
- 否则若另一个集合的大小 \(< \lfloor\frac{100}{p}\rfloor\),则直接插入。
- 否则若另一个集合里 \(c\) 的最小值大于当前这个二元组的 \(c\) 值,则将集合内所有的 \(c\) 值减去当前二元组的 \(c\) 值。
- 否则将 \(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
*/