题意
一个数列,m次查询。每次询问一个区间。要把该区间拆成若干个出现次数最多的数出现次数小于等于 c e i l ( n / 2 ) ceil(n/2) ceil(n/2)的子序列,问最少拆成多少个子序列。
思路
若出现次数最多的数的出现次数小于等于
c
e
i
l
(
n
/
2
)
ceil(n/2)
ceil(n/2),答案显然是1。对于其他情况,有一种拆分方式:设出现最多的数是super,其出现次数为f。其他数一共有m-f个(m为区间长度)。拆成
1
+
(
f
−
(
m
−
f
+
1
)
)
1+(f-(m-f+1))
1+(f−(m−f+1))个,即
m
−
f
+
1
m-f+1
m−f+1个super用来与那
m
−
f
m-f
m−f个其他数凑成一个子序列。剩下的super自成一个个子序列。由于多1是尽可能大的拆分方式了,因此没有更好的解。
实现
方法一:莫队
应用:离线查询区间众数出现次数。(数值大需要离散化)
#include<bits/stdc++.h>
using namespace std;
int n,m,maxn;
const int N = 3e5+5;
int a[N],cnt[N],sum[N],len[N]; // 每个数出现的次数, 每个出现次数的出现次数
int f; // frequency
struct node{
int l,r,id;
bool operator < (const node &b) const{
if(l/maxn != b.l/maxn) return l<b.l;
return r<b.r;
}
}q[N];
void add(int i){
--sum[cnt[a[i]]];
++cnt[a[i]];
if(cnt[a[i]]>f) f=cnt[a[i]];
++sum[cnt[a[i]]];
}
void del(int i){
--sum[cnt[a[i]]];
if(f==cnt[a[i]]&&!sum[cnt[a[i]]]) --f; // f:出现最多的数的出现次数
--cnt[a[i]];
++sum[cnt[a[i]]];
}
int ans[N];
int main(){
scanf("%d%d",&n,&m);
maxn = sqrt(n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=0;i<m;++i) scanf("%d%d",&q[i].l,&q[i].r),q[i].id = i,len[i]=q[i].r-q[i].l+1;
sort(q,q+m);
f=0;
for(int i=0,l=1,r=0;i<m;++i) {
while(l>q[i].l) add(--l);
while(r<q[i].r) add(++r);
while(l<q[i].l) del(l++);
while(r>q[i].r) del(r--);
ans[q[i].id] = f;
}
for(int i=0;i<m;++i){
int L = len[i];
if(ans[i]<=(L+1)/2) cout<<1<<endl;
else cout<<ans[i]-(L-ans[i])<<endl;
}
}
方法二:随机化
学习了一种随机化操作
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+5;
int a[N];
#define all(v) (v).begin(),(v).end()
vector<int> v[N];
int main(){
int n,q;scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
v[a[i]].push_back(i);
}
for(int i=1;i<=q;++i){
int l,r;scanf("%d%d",&l,&r);
int ans = 0;
for(int j=1;j<=40;++j){
int id = a[rand()*rand()%(r-l+1)+l];
ans = max(ans,int(upper_bound(all(v[id]),r)-lower_bound(all(v[id]),l)));
}
cout<<max(1,ans+ans-(r-l+1))<<endl;
}
}
方法三:主席树求众数
刚学就用到了。。
超过半数的众数显然也是中位数。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6+5;
int A[maxn],root[maxn],cnt=1;
int c[maxn],L[maxn],ori[maxn];
#define num(x) tree[x].num
#define ls(x) tree[x].ls
#define rs(x) tree[x].rs
struct{
int num;
int ls,rs;
}tree[maxn<<2];
void build(){
root[0] = 1;
num(1) = 0;
ls(1)=1;
rs(1)=1;
}
void update(int a,int l,int r,int rt,int nrt){
num(nrt) = num(rt);
if(l==r) num(nrt)++;
else{
ls(nrt) = ls(rt),rs(nrt) = rs(rt);
int mid = (l+r)>>1;
if(a<=mid) ls(nrt)=++cnt,update(a,l,mid,ls(rt),ls(nrt));
else rs(nrt)=++cnt,update(a,mid+1,r,rs(rt),rs(nrt));
num(nrt) = num(ls(nrt)) + num(rs(nrt));
}
}
// 查询区间第a大
int query(int a,int l,int r,int p,int q){
// cout<<a<<" "<<l<<" "<<r<<" "<<p<<" "<<q<<endl;
if(l==r) return num(q)-num(p); // 该数的出现次数
else{
int mid = (l+r)>>1;
if(a<=num(ls(q))-num(ls(p))) return query(a,l,mid,ls(p),ls(q));
else return query(a-(num(ls(q))-num(ls(p))),mid+1,r,rs(p),rs(q));
}
}
int discretize(int n){
for(int i=0;i<n;++i) c[i]=A[i];
sort(c,c+n);
int len = unique(c,c+n)-c;
int maxx = 0;
for(int i=0;i<n;++i){
L[i] = lower_bound(c,c+len,A[i])-c+1; // 查找
maxx = max(maxx,L[i]);
ori[L[i]] = A[i]; // 离散化后的数对应的原数
}
return maxx;
}
int main(){
int n,m;scanf("%d%d",&n,&m);
for(int i=0;i<n;++i) scanf("%d",&A[i]);
build();
int maxx = discretize(n);
for(int i=0;i<n;++i){
root[i+1] = ++cnt;
update(L[i],1,maxx,root[i],root[i+1]);
}
for(int i=1;i<=m;++i){
int l,r;scanf("%d%d",&l,&r);
int ans = query(((l+r)>>1)-l+1,1,maxx,root[l-1],root[r]); // 查询中位数
// cout<<ans<<endl;
cout<<max(1,ans-(r-l+1-ans))<<endl;
}
}