https://codeforces.ml/contest/1514/problem/D
题意
给定一个长度为n的数组以及q组询问,每组询问包括l和r。希望将a[l]~a[r]分为若干子集,使得每一子集(长度为x)所有元素出现个数都小于等于⌈ x 2 \frac{x}{2} 2x⌉
思路
线段树:
- 用线段树维护区间出现频率最大的数(不准确)
- 对于一个数在[l,r]区间出现多少次可以用vector记录某个数在数组中的位置,用二分的方式就可以O(logn)得到答案(这个操作感觉还是实用的)
- 通过mymax函数比较两个数哪个出现频率高,并返回出现频率高的数
- 若出现次数f小于等于 ⌈
x
2
\frac{x}{2}
2x⌉,则返回1。否则,剩余元素为x-f,由于向上取整,最多可以和x-f+1个最大频率元素结合,因此返回f-(x-f+1)+1=2*f-x
那么问题来了,线段树建树向上更新父节点时,难道父节点所代表的区间出现最高频率的数一定是两个儿子节点出现最高频率数中的一个吗?
显然不是,例如2,2,2,3,3,1,1,1,3,3
前半段出现最高频率数为2,后边段为1,而整段却为3
那为什么又能这样写呢?
若是父节点不是两个子节点之一,那么父节点的值出现次数一定小于等于⌈ x 2 \frac{x}{2} 2x⌉,因此对结果无影响
莫队
询问顺序无所谓,因此可以离线,看作一个区间众数问题,用莫队求出区间众数出现次数
主席树好像可以在线做,可惜我不会
代码
线段树
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int maxn=3e5+5;
const ll mod=1e9+7;
int a[maxn],tree[maxn<<2];
vector<int> vec[maxn];
int n,q;
int get(int x,int l,int r){
return upper_bound(vec[x].begin(),vec[x].end(),r)-lower_bound(vec[x].begin(),vec[x].end(),l);
}
int mymax(ll x,ll y,ll l,ll r){
ll t1=get(x,l,r),t2=get(y,l,r);
if(t1>t2)return x;
return y;
}
void build(int p,int l,int r){
if(l==r){
tree[p]=a[l];
return;
}
ll mid=(l+r)>>1;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
tree[p]=mymax(tree[2*p],tree[2*p+1],l,r);
}
int query(int p,int l,int r,int x,int y){
if(x<=l&&y>=r){
return get(tree[p],x,y);
}
int mid=(l+r)>>1,t1=0,t2=0;
if(x<=mid)t1=query(2*p,l,mid,x,y);
if(y>mid)t2=query(2*p+1,mid+1,r,x,y);
return max(t1,t2);
}
int main() {
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
vec[a[i]].pb(i);
}
build(1,1,n);
while(q--){
int l,r;cin>>l>>r;
cout<<max(1,2*query(1,1,n,l,r)-(r-l+1))<<endl;
}
}
莫队
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int maxn=3e5+5;
struct query{
int id,l,r,bl;
}q[maxn];
//num为数i出现次数,t为出现次数为i的数的个数,a为原数组
int num[maxn],a[maxn],t[maxn];
int ans[maxn]; //答案
int cnt,sz; //cnt是当前区间众数出现次数
bool cmp(query a,query b){
return a.bl==b.bl?a.r<b.r:a.bl<b.bl;
}
void add(int i){
t[num[a[i]]]--;
num[a[i]]++;
t[num[a[i]]]++;
cnt=max(cnt,num[a[i]]);
}
void del(int i){
t[num[a[i]]]--;
if(cnt==num[a[i]]&&t[cnt]==0)cnt--;
num[a[i]]--;
t[num[a[i]]]++;
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n,m;cin>>n>>m;
sz=sqrt(n);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++){
int l,r;cin>>l>>r;
q[i]=(query){i,l,r,(l-1)/sz+1};
}
sort(q+1,q+m+1,cmp);
int l=1,r=0;
for(int i=1;i<=m;i++){
int ll=q[i].l,rr=q[i].r;
// cout<<ll<<' '<<rr<<' '<<q[i].bl<<endl;
while(l<ll)del(l++);
while(l>ll)add(--l);
while(r<rr)add(++r);
while(r>rr)del(r--);
ans[q[i].id]=max(2*cnt-(rr-ll+1),1);
}
for(int i=1;i<=m;i++)cout<<ans[i]<<endl;
}