主席树 ---- 2021 ICPC 昆明 M.Stone Games [主席树 + 暴力]

题目链接


题目大意:

给你一个数字序列
就是每次查询一个区间问你这个区间中的数不能组合成的数的最小是多少?


解题思路:

1.首先我们知道假设这个区间里面没有1那么 a n s = 1 ans=1 ans=1
2.对于区间里面的数
b 1 < b 2 < b 3 . . < b n b_1<b_2<b_3..<b_n b1<b2<b3..<bn
s u m [ i ] = ∑ i = 1 n b [ i ] sum[i]=\sum_{i=1}^nb[i] sum[i]=i=1nb[i]
3.假如区间里面前 i i i个数 ( b 1 , 2 , 3... i ) (b_{1,2,3...i}) (b1,2,3...i)可以表示 [ 1 , x ] [1,x] [1,x]里面所有的数,那么接下来我们据要看是否可以表示出 x + 1 x+1 x+1,假如说 b i + 1 > x + 1 b_{i+1}>x+1 bi+1>x+1的话那么无论它怎么和前面的数组合都是大过 x + 1 x+1 x+1那么答案就是 x + 1 x+1 x+1,假如 b i + 1 < x + 1 b_{i+1}<x+1 bi+1<x+1因为前面是 ∈ [ 1 , x ] 那 么 就 有 x + 1 − b i + 1 这 个 数 在 里 面 就 一 定 可 以 凑 出 x + 1 \in[1,x]那么就有x+1-b_{i+1}这个数在里面就一定可以凑出x+1 [1,x]x+1bi+1x+1,那么对于是否可以凑出 x + 1 x+1 x+1实际上就是在 所 有 小 于 x + 1 的 b i 之 和 是 否 超 过 b i 所有小于x+1的b_i之和是否超过b_i x+1bibi
4.那么我们就可以暴力枚举 那个 x + 1 x+1 x+1, 假 如 已 经 有 [ 1 , x ] 再 来 个 x + 1 就 可 以 表 示 的 数 会 变 成 [ 1 , 2 ∗ x + 1 ] 这 个 增 长 接 近 指 数 的 那 么 就 是 近 乎 l o g 级 别 的 枚 举 是 可 以 接 受 假如已经有[1,x]再来个x+1就可以表示的数会变成[1,2*x+1]这个增长接近指数的那么就是近乎log级别的枚举是可以接受 [1,x]x+1[1,2x+1]log
5.对于询问某段区间里面小于某个值的和我们可以用 主 席 树 主席树 因为这个具有前缀和性质。


#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;
struct BIT {
    int lson, rson;
    ll sum;
}tr[maxn * 40];
int idx;
vector<ll> lis;
int n, m;
ll num[maxn];
int root[maxn];

int getid(ll x) {
    if(x >= lis[lis.size()-1]) return lis.size();
    return lower_bound(lis.begin(),lis.end(),x)-lis.begin();
}

inline void insert(int &rt, int l, int r, int pos, int pre, int val) {
    rt = ++ idx;
    tr[rt] = tr[pre];   
    if(l == r) {
        tr[rt].sum += val;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) insert(tr[rt].lson,l,mid,pos,tr[pre].lson, val);
    else insert(tr[rt].rson,mid+1,r,pos,tr[pre].rson, val);
    tr[rt].sum = tr[tr[rt].lson].sum + tr[tr[rt].rson].sum;
}

inline ll ask(int pos, int l, int r, int ltree, int rtree) { 
    if(l == r) {
        return tr[rtree].sum - tr[ltree].sum;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) return ask(pos,l,mid,tr[ltree].lson,tr[rtree].lson);
    else return ask(pos,mid+1,r,tr[ltree].rson,tr[rtree].rson) + tr[tr[rtree].lson].sum - tr[tr[ltree].lson].sum;
}


int main() {

    //.........................
    lis.push_back(-INF);
    scanf("%d %d",&n,&m);
    for(int i = 1; i <= n; ++ i) {
        scanf("%lld",&num[i]);
        lis.push_back(num[i]);
    }
    sort(lis.begin(),lis.end());
    lis.erase(unique(lis.begin(),lis.end()),lis.end());

    for(int i = 1; i <= n; ++ i) 
      insert(root[i],1,n,getid(num[i]),root[i-1],num[i]);
    ll ans = 0;
    
    while(m --) {
       int l , r;
       scanf("%d %d",&l,&r);
       l = (l+ans)%n+1; 
       r = (r+ans)%n+1;//坑点
       if(l > r) swap(l,r);
       ans = 0;
       while(1) {
          int p = getid(ans+1);//注意离散化之后求比ans+1小的数的下标
          if(lis[p] > ans + 1) p --;//如果不是向下取整
          if(!p) break
          ll t = ask(p,1,n,root[l-1],root[r]);
          if(t == ans) break;
          ans = t;
       }  
       printf("%lld\n",ans+1);
       ans += 1;
    }

    return 0;
}
/*
5 5
1 4 2 1 6
1 3
2 1
2 4
1 4
3 4
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值