题解:洛谷 P3567([POI 2014] KUR-Couriers)
1. Description
定义一个长度为 \(len\) 的序列的绝对众数为:出现次数大于 \(\frac {len}2\) 的数值,现在给定一个长度为 \(n\) 的序列 \(a\),并给定 \(m\) 个询问,每一次询问 \(a\) 的子序列 \([l,r]\) 的绝对众数,如果绝对众数不存在,那么输出 \(0\)。
2. Solution
这里给出三种做法,其中一种是离线做法,另外两种是在线做法。
1.分治的离线做法
看到绝对众数,我们可以想到另外一道题,因此想到分治做法。
先将所有询问离线,假设现在我们分治递归到了 \([l,r]\) 这个区间,处理左端在 \([l,mid]\),右端在 \([mid+1,r]\) 的询问,那么可能在 \([l,r]\) 这个区间中的任意一个子区间内成为绝对众数的数其实十分少,只有 \(\log n\) 个,我们可以直接记录下来所有可能成为绝对众数的数,然后对于所有询问,我们检查每一个数 \(x\) 是否是询问区间的绝对众数即可,具体的检查方法为,将 \(x\) 视为 \(1\),将其他数视为 \(-1\),然后看看区间和是不是大于零即可,可以使用前缀和 \(O(len)\) 预处理,然后 \(O(1)\) 查询。
时间复杂度不太确定,但是跑的还是挺快的。
2.线段树的在线做法
求绝对众数,我们可以使用摩尔投票法(不知道摩尔投票法可以百度一下),那么我们使用线段树维护每一个区间的摩尔投票的结果,不过需要注意的是,当绝对众数不存在的时候,摩尔投票法得到的结果是不正确的,所以得到结果之后一定要检查一下是否合法,具体的,使用 vector 记录每一个数值出现的位置,然后二分求出区间中这个数值的出现次数,时间复杂度为 \(O((m+n)\log n)\)。
3.主席树的在线做法
首先我们对序列按照权值建出主席树,然后考虑对于一个区间 \([l,r]\) 如何求解,分类讨论。
- 如果现在已经到了叶子节点,那么直接返回。
- 如果左儿子出现的数的个数大于 \(\frac{r-l+1}{2}\),那么向左儿子递归。
- 如果右儿子出现的数的个数大于 \(\frac{r-l+1}{2}\),那么向右儿子递归。
- 否则,返回 \(0\)。
3. Code
1.分治的离线做法
/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=5e5+5;
int n,m;
int a[N],ans[N],cnt[N],pre[N];
bool vis[N];
vector<int>val;
struct Query{
int l,r,id;
}b[N],c[N];
vector<Query>now;
void count(int x,int l,int r){
pre[l-1]=0;
for(int i=l;i<=r;i++)pre[i]=pre[i-1]+(a[i]==x?1:-1);
for(Query tmp:now)
if(pre[tmp.r]-pre[tmp.l-1]>0)
ans[tmp.id]=x;
}
void solve(int l,int r,int ql,int qr){
if(ql>qr)return ;
if(l==r)return ;
int mid=l+r>>1;
now.clear();
val.clear();
int tmpql=ql-1,tmpqr=qr+1;
for(int i=ql;i<=qr;i++){
if(b[i].r<=mid)c[++tmpql]=b[i];
else if(b[i].l>mid)c[--tmpqr]=b[i];
else now.push_back(b[i]);
}
for(int i=mid;i>=l;i--){
cnt[a[i]]++;
if(cnt[a[i]]*2>(mid-i+1)&&!vis[a[i]]){
vis[a[i]]=1;
val.push_back(a[i]);
}
}
for(int i=mid;i>=l;i--)cnt[a[i]]--;
for(int i=mid+1;i<=r;i++){
cnt[a[i]]++;
if(cnt[a[i]]*2>(i-mid)&&!vis[a[i]]){
vis[a[i]]=1;
val.push_back(a[i]);
}
}
for(int i=mid+1;i<=r;i++)cnt[a[i]]--;
for(int x:val){
count(x,l,r);
vis[x]=0;
}
for(int i=ql;i<=tmpql;i++)b[i]=c[i];
for(int i=qr;i>=tmpqr;i--)b[i]=c[i];
solve(l,mid,ql,tmpql),solve(mid+1,r,tmpqr,qr);
}
signed main(){
read(n),read(m);
for(int i=1;i<=n;i++)read(a[i]);
int tmp=0;
for(int i=1,l,r;i<=m;i++){
read(l),read(r);
if(l==r){
ans[i]=a[l];
continue;
}
b[++tmp]={l,r,i};
}
solve(1,n,1,tmp);
for(int i=1;i<=m;i++)write(ans[i]),Nxt;
}
2.线段树的在线做法
/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=5e5+5;
int n,m;
int a[N];
vector<int>place[N];
struct Node{
int x,cnt;
Node friend operator +(Node a,Node b){
if(a.x==0||b.x==0)return {a.x+b.x,a.cnt+b.cnt};
if(a.x==b.x)return {a.x,a.cnt+b.cnt};
if(a.cnt>b.cnt)return {a.x,a.cnt-b.cnt};
if(a.cnt<b.cnt)return {b.x,b.cnt-a.cnt};
return {0,0};
}
};
struct Segment_tree{
Node c[N<<2];
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
void pushup(int p){
c[p]=c[ls]+c[rs];
}
void build(int p,int l,int r){
if(l==r){
c[p]={a[l],1};
return ;
}
build(ls,l,mid),build(rs,mid+1,r);
pushup(p);
}
Node query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return c[p];
if(mid>=L&&mid<R)return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
if(mid>=L)return query(ls,l,mid,L,R);
return query(rs,mid+1,r,L,R);
}
}Set;
int count(int l,int r,int x){
auto R=upper_bound(place[x].begin(),place[x].end(),r)-1;
auto L=lower_bound(place[x].begin(),place[x].end(),l);
return R-L+1;
}
signed main(){
read(n),read(m);
for(int i=1;i<=n;i++){
read(a[i]);
place[a[i]].push_back(i);
}
Set.build(1,1,n);
for(int i=1,l,r,ans;i<=m;i++){
read(l),read(r);
Node tmp=Set.query(1,1,n,l,r);
if(tmp.x==0||count(l,r,tmp.x)*2<=r-l+1)puts("0");
else write(tmp.x),Nxt;
}
}
3.主席树的在线做法
const int N=5e5+5;
int n,m;
int rt[N];
struct Segment_tree{
int num;
int ls[N<<5],rs[N<<5],cnt[N<<5];
#define mid (l+r>>1)
int New(){
num++;
ls[num]=rs[num]=cnt[num]=0;
return num;
}
int copy(int p){
int q=New();
ls[q]=ls[p],rs[q]=rs[p],cnt[q]=cnt[p];
return q;
}
void pushup(int p){
cnt[p]=cnt[ls[p]]+cnt[rs[p]];
}
int build(int l,int r){
int p=New();
if(l==r)return p;
ls[p]=build(l,mid),rs[p]=build(mid+1,r);
return p;
}
int change(int p,int l,int r,int x,int v){
int q=copy(p);
if(l==r){
cnt[q]+=v;
return q;
}
if(mid>=x)ls[q]=change(ls[p],l,mid,x,v);
else rs[q]=change(rs[p],mid+1,r,x,v);
pushup(q);
return q;
}
int query(int p,int q,int l,int r,int len){
if(l==r)return l;
if(2*(cnt[ls[q]]-cnt[ls[p]])>len)return query(ls[p],ls[q],l,mid,len);
if(2*(cnt[rs[q]]-cnt[rs[p]])>len)return query(rs[p],rs[q],mid+1,r,len);
return 0;
}
void print(int p,int l,int r){
if(l==r)return ;
print(ls[p],l,mid);
print(rs[p],mid+1,r);
}
}Set;
signed main(){
read(n),read(m);
rt[0]=Set.build(1,n);
for(int i=1,x;i<=n;i++){
read(x);
rt[i]=Set.change(rt[i-1],1,n,x,1);
}
Set.print(rt[n],1,n);
for(int i=1,l,r;i<=m;i++){
read(l),read(r);
write(Set.query(rt[l-1],rt[r],1,n,r-l+1)),Nxt;
}
}

浙公网安备 33010602011771号