「计蒜之道」 2020-线上决赛 C-攀登山峰 //主席树上二分

题目链接

https://nanti.jisuanke.com/t/49111

题意

n n n 个数, m m m 个操作,每个操作给3个数: l   r   t l \ r \ t l r t,让你输出这个区间满足个数超过 r − l + 1 t \frac{r-l+1}{t} trl+1的最大的数。

思路

查询区间满足一定条件数,首先想到主席树,根据原序列建立主席树(可持久化权值线段树),查询的时候,优先去右边找,右边找不到再去左边找,这样能保证找到的数最大.

代码

#include <bits/stdc++.h>
#define ft first
#define sd second
#define endl '\n'
#define pb push_back
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<ll,string> P;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
const int mod=998244353;
const int N=1e9;
int n,m;
struct node{
    int l,r,sum;
}no[maxn*40];
int cnt,root[maxn],a[maxn];
void add(int l,int r,int pre,int &now,int x){
    now=++cnt;
    no[now]=no[pre];
    ++no[now].sum;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)add(l,mid,no[pre].l,no[now].l,x);
    else add(mid+1,r,no[pre].r,no[now].r,x);
}
//主席树上二分找最右边满足要求的数,这个数肯定是最大的
int query(int l,int r,int pre,int now,int x){
    if(l==r){//看该数是否>=x,不是就按题意返回-1
        if(no[now].sum-no[pre].sum<x)return -1;
        else return l;
    }
    int mid=(l+r)>>1,ans=-1;
    int sumR=no[no[now].r].sum-no[no[pre].r].sum;
    //先去右边找
    if(sumR>=x)ans=query(mid+1,r,no[pre].r,no[now].r,x);
    if(ans!=-1)return ans;
    else {//右边没找到再去左边找
        ans=query(l,mid,no[pre].l,no[now].l,x);
        return ans;
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        add(1,N,root[i-1],root[i],a[i]);
    }
    while(m--){
        int l,r,t;
        scanf("%d%d%d",&l,&r,&t);
        int x=(r-l+1)/t+1;//要求大于,那就向下取整加一
        printf("%d\n",query(1,N,root[l-1],root[r],x));
    }
    return 0;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页