CF840D Destiny

Description

给定 \(k\leq 5\),求区间 \([l,r]\) 内出现次数严格大于 \(\frac{r-l+1}{k}\) 的最小的数,没有就输出 -1。

Solution

抓住区间总共只有 \(r-l+1\) 个数的特性,而 \(k\) 很小,那么出现次数很大的数一定不多,实际上严格小于 \(k\)。所以用主席树维护出现次数的和,如果某个值域的出现次数大于该阈值就递归下去。会发现每层被访问的节点不超过 \(k\) 个。那么单次询问复杂度就是 \(O(k\log n)\)

#include<stdio.h>
#include<algorithm>
using namespace std;

typedef long long ll;

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

const int N=3e5+7;

struct Node{
    int ls,rs,s;
}t[N*40];

#define lid t[id].ls
#define rid t[id].rs

int cnt=0,rt[N];
void build(int &id,int lf,int rf){
    id=++cnt;
    if(lf==rf) return ;
    int mid=(lf+rf)>>1;
    build(lid,lf,mid);
    build(rid,mid+1,rf);
}

int Val;
void modify(int &id,int pre,int lf,int rf){
    id=++cnt;
    t[id]=t[pre]; t[id].s++;
    if(lf==rf) return;
    int mid=(lf+rf)>>1;
    if(Val<=mid) modify(lid,t[pre].ls,lf,mid);
    else modify(rid,t[pre].rs,mid+1,rf);
}

int query(int id,int pre,int lf,int rf){
    if(lf==rf) return t[id].s-t[pre].s>=Val? lf:-1;
    int mid=(lf+rf)>>1,ret=-1;
    int s=t[lid].s-t[t[pre].ls].s;
    if(s>=Val) ret=query(lid,t[pre].ls,lf,mid);
    if(~ret) return ret;
    s=t[rid].s-t[t[pre].rs].s;
    return s>=Val? query(rid,t[pre].rs,mid+1,rf):-1;
}

int main(){
    int n=read(),Q=read();
    build(rt[0],1,n);
    for(int i=1;i<=n;i++)
        Val=read(),modify(rt[i],rt[i-1],1,n);
    while(Q--){
        int l=read(),r=read(),k=read();
        Val=(r-l+1)/k+1,printf("%d\n",query(rt[r],rt[l-1],1,n));
    }
}
posted @ 2021-08-18 09:39  Kreap  阅读(31)  评论(0编辑  收藏  举报