【2018.07.03NOIP模拟】T2 Problem B
Description
给一个长度为n 的序列a。m 组询问,每次询问一个区间[l,r],是否存在一个数
在[l,r]中出现的次数大于(r-l+1)/2。如果存在,输出这个数,否则输出0。
Input
第一行两个数n,m。
第二行n 个数,a[i]。
接下来m 行,每行两个数l,r,表示询问[l,r]这个区间。
Output
m 行,每行对应一个答案。
Sample input
7 4
1 1 3 2 1 2 1
3 3
1 5
1 6
4 6
Sample output
3102
Constraint
30% n,m<=10
另20% ai<=10
100% n,m<=100000,1<=ai<=n
正解主席树,用数组下表顺序来建立主席树,然后可以选定区间后在主席树上二分,如果左儿子siz大于要求,向左儿子跳,如果右儿子siz大于要求,向右儿子跳,否则返回0
#include<bits/stdc++.h>
using namespace std;
#define N 100010
int rt[N],ls[N*20],rs[N*20],siz[N*20],cnt=0;
int n,m,a[N];
void build(int &rt,int l,int r){
rt=++cnt;
if(l==r)return;
int mid=(l+r)>>1;
build(ls[rt],l,mid);
build(rs[rt],mid+1,r);
}
void insert(int &rt,int last,int l,int r,int pos){
rt=++cnt;
ls[rt]=ls[last];
rs[rt]=rs[last];
if(l==r){siz[rt]=siz[last]+1;return;}
int mid=(l+r)>>1;
if(pos<=mid)insert(ls[rt],ls[last],l,mid,pos);
else insert(rs[rt],rs[last],mid+1,r,pos);
siz[rt]=siz[ls[rt]]+siz[rs[rt]];
}
int query(int rt1,int rt2,int l,int r,int len){
if(l==r)return l;
int mid=(l+r)>>1;
if(siz[ls[rt1]]-siz[ls[rt2]]>len)return query(ls[rt1],ls[rt2],l,mid,len);
if(siz[rs[rt1]]-siz[rs[rt2]]>len)return query(rs[rt1],rs[rt2],mid+1,r,len);
return 0;
}
int main(){
scanf("%d%d",&n,&m);
build(rt[0],1,n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(rt[i],rt[i-1],1,n,a[i]);
}
for(int i=1;i<=m;i++){
int l,r;scanf("%d%d",&l,&r);
printf("%d\n",query(rt[r],rt[l-1],1,n,(r-l+1)/2));
}
return 0;
}
然后请教yyf大佬后学会了回滚莫队算法,就不详细讲了,大概是每次暴力跳左指针,并单调移动右指针,时间可以保证是
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define N 100010
struct Node{int l,r,id;}p[N];
int n,m,siz,a[N];
int block[N],ans[N],cnt[N],add[N];
bool cmp(Node a,Node b){
if(block[a.l]==block[b.l])return a.r<b.r;
return a.l<b.l;
}
int work(int l,int r){
memset(cnt,0,sizeof(cnt));
int res=a[l];
for(int i=l;i<=r;i++)
if(++cnt[a[i]]>=cnt[res])res=a[i];
return res;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++){
scanf("%d%d",&p[i].l,&p[i].r);
p[i].id=i;
}
siz=sqrt(n);
for(int i=1;i<=n;i++)block[i]=(i-1)/siz+1;
sort(p+1,p+m+1,cmp);
int lastans=0,last=0;
for(int i=1;i<=m;i++){
if(block[p[i].l]==block[p[i].r]){
int tmp=work(p[i].l,p[i].r);
ans[p[i].id]=(cnt[tmp]>(p[i].r-p[i].l+1)/2)?tmp:0;
continue;
}
int nowans;
if(!last||block[p[i].l]!=block[p[last].l])
nowans=work(siz*block[p[i].l]+1,p[i].r);
else{
nowans=lastans;
for(int j=p[last].r+1;j<=p[i].r;j++)
if(++cnt[a[j]]>cnt[nowans])nowans=a[j];
}
lastans=nowans;last=i;
for(int j=p[i].l;j<=siz*block[p[i].l];j++)
if(++cnt[a[j]]>cnt[nowans])nowans=a[j];
ans[p[i].id]=(cnt[nowans]>(p[i].r-p[i].l+1)/2)?nowans:0;
for(int j=p[i].l;j<=siz*block[p[i].l];j++)--cnt[a[j]];
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}