【LOJ】#2174. 「FJOI2016」神秘数

本文介绍了一种使用主席树解决区间求和问题的方法,通过动态维护区间内元素的排序来快速查找满足条件的最小值。算法的时间复杂度为O(MlogNlog∑a_i),适用于需要频繁查询区间和的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

这道题的结论很显然= =
就是暴力求的话,把一个区间的数排一下序,如果当前这个数大于前面所有数的前缀和+1,那么前缀和+1即我们所求的答案

那么我们设置一个当前答案(初始为1),在主席树上求出来小于这个答案的数的和是多少,设为t,如果t < ans,那么答案就是ans,如果t >= ans,那么设置ans = t + 1

容易发现,在两次操作之后ans必然翻倍,所以复杂度是\(O(M \log N \log \sum a_{i})\)

代码

#include <bits/stdc++.h>
#define MAXN 100005
//#define ivorysi
#define enter putchar('\n')
#define space putchar(' ')
#define fi first
#define se second
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
    if(c == '-') f = -1;
    c = getchar();
    }
    while(c >= '0' && c <= '9') {
    res = res * 10 + c - '0';
    c = getchar();
    }
    res *= f;
}
template<class T>
void out(T x) {
    if(x < 0) {putchar('-');x = -x;}
    if(x >= 10) {
    out(x / 10);
    }
    putchar('0' + x % 10);
}
struct node {
    int sum;
    int lc,rc;
}tr[MAXN * 40];
int Ncnt,rt[MAXN];
int N,a[MAXN],M,MK;
void Insert(const int &x,int &y,int L,int R,int v) {
    y = ++Ncnt;
    tr[y] = tr[x];
    tr[y].sum += v;
    if(L == R) return;
    int mid = (L + R) >> 1;
    if(v <= mid) Insert(tr[x].lc,tr[y].lc,L,mid,v);
    else Insert(tr[x].rc,tr[y].rc,mid + 1,R,v);
}
void Init() {
    read(N);
    for(int i = 1 ; i <= N ; ++i) read(a[i]),MK = max(MK,a[i]);
    read(M);
    for(int i = 1 ; i <= N ; ++i) {
    Insert(rt[i - 1],rt[i],1,MK,a[i]);
    }
}
int query(int L,int R,int v) {
    L = rt[L - 1],R = rt[R];
    int l = 1,r = MK,res = 0;
    while(1) {
    if(l == r) {res += tr[R].sum - tr[L].sum;break;}
    int mid = (l + r) >> 1;
    if(v <= mid) {
        r = mid;
        L = tr[L].lc;R = tr[R].lc;
    }
    else {
        l = mid + 1;
        res += tr[tr[R].lc].sum - tr[tr[L].lc].sum;
        L = tr[L].rc;R = tr[R].rc;
    }
    }
    return res;
}
void Solve() {
    Init();
    int L,R;
    while(M--) {
    read(L);read(R);
    int ans = 1;
    while(1) {
        int t = query(L,R,ans);
        if(t >= ans) ans = t + 1;
        else break;
    }
    out(ans);enter;
    }
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
    return 0;
}

转载于:https://www.cnblogs.com/ivorysi/p/9151353.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值