[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;
}