ICPC昆明 M-Stone Game 可持久化权值线段树维护区间和

题目大意:
给定[l,r]区间,区间内的数字任意组合。求最小不能被表示出来的数字。
思路:

  1. 如果没有元素1,则答案就是1
  2. 我们设当前能表示出来的最大整数是x,那么在小于等于x的元素中,任意选出一个元素b。x+b中的元素都可以表示出来。
  3. 所以我们只需要从1开始,枚举当前答案的前缀和。如果前缀和小于当前答案。那么该答案就是正确答案。

举例:
1 20 12 2 5 2,查询[1,6]能表出的最小整数。
ans = 1 , sum = 1
ans = 2 , sum = 5 , 观察1,2,2序列,可以表示出1,2,3,4,5的值。
ans = 5 , sum = 10 , 在1,2,2的基础上,加上5,则可以表示1-10的任意元素
ans = 10 , sum = 10
ans = 11 , sum = 10 则不能表示出11

代码实现:

#include <bits/stdc++.h>
using namespace std;
#define ll long long

const ll maxn = 1e6+100;

ll tot,rt[maxn];
struct node{ll ls,rs,sum;}t[maxn<<5];
ll a[maxn],b[maxn],n,m,len;

ll getid(ll val){return lower_bound(b+1,b+len+1,val)-b;}

ll build(ll l,ll r){
    ll now = ++tot;
    t[now].sum = 0;
    if(l==r) return now;
    ll mid = l+r>>1;
    t[now].ls = build(l,mid);
    t[now].rs = build(mid+1,r);
    return now;
}

ll update(ll l,ll r,ll rt,ll pos,ll val){
    ll now = ++tot;
    t[now] = t[rt] , t[now].sum+=val;//与一般权值线段树不同,维护前缀和
    if(l==r) return now;
    ll mid = l+r>>1;
    if(pos<=mid) t[now].ls = update(l,mid,t[rt].ls,pos,val);
    else t[now].rs = update(mid+1,r,t[rt].rs,pos,val);
    return now;
}

ll query(ll u,ll v,ll l,ll r,ll pos){//<=pos sum
    ll mid = l+r>>1;
    ll k = t[t[u].ls].sum-t[t[v].ls].sum;
    if(l==r) return t[u].sum-t[v].sum;
    if(pos<=mid) return query(t[u].ls,t[v].ls,l,mid,pos);
    else return query(t[u].rs,t[v].rs,mid+1,r,pos)+k;
}

int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&a[i]) , b[i] = a[i];
    sort(b+1,b+n+1);
    len = unique(b+1,b+n+1)-b-1;	//离散化
    rt[0] = build(1,len);
    for(ll i=1;i<=n;i++)
        rt[i] = update(1,len,rt[i-1],getid(a[i]),a[i]);
    ll ans=0,l,r;
    while(m--){
        scanf("%lld%lld",&l,&r);
        l = (l+ans)%n+1;
        r = (r+ans)%n+1;
        if(l>r) swap(l,r);
        ll newans = 1;	//当前答案
        while(1){
            ll pos = getid(newans),tmp;	
            if(b[pos]!=newans) pos--;
            //在序列 1 2 3 5中查询4的id时
            //geid()返回的是5,而我们要求的是小于等于newans的答案。
            //所以要特判将id--
            if(!pos) break;	//wa了两发
            tmp = query(rt[r],rt[l-1],1,len,pos);	
            //查询下一次能表示的最大值
            if(tmp>=newans) newans = tmp+1;	//查询下一个
            else break;	//当前前缀和不足以表示当前答案,则答案为newans
        }
        printf("%lld\n",newans);
        ans = newans;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值