【2018.07.03NOIP模拟】T2 Problem B 【主席树】【回滚莫队】

【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大佬后学会了回滚莫队算法,就不详细讲了,大概是每次暴力跳左指针,并单调移动右指针,时间可以保证是 O ( n s q r t ( n ) )

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

猜你喜欢

转载自blog.csdn.net/Dream_maker_yk/article/details/80904438