2021 ICPC亚洲区域赛 昆明 M.Stone Games(主席树)

题面

image-20210410231815658

题意

​ n个数,分别是 \(s_1,s_2,s_3 \dots s_n\) ,以及Q个询问,对于每个询问,有两个整数 \(l\) 和 \(r\) ,问对于区间 \(s_l \dots s_r\) ,从该区间任意取数求和无法得到的最小的正整数。

思路

​ 显然, 当该区间没有1时答案为1。 我们假设该区间已经取了某些数可以相加得到 \([1,x]\) ,对于该区间未取的数, 若存在 \(x+1\) ,则可将 \(x+1\) 合并进答案,使我们从该区间取数相加可得的范围变为 \([1,2x+1]\) , 反之,若该区间不存在 \(x+1\) , 则答案就是 \(x+1\) 。

​ 每个询问开始时, 首先查找区间内有多少个1, 也就是该区间内数字1的和, 假设和为 \(x\) ,代表该区间内至少可以组合出 \([1,x]\) 的数,重复上述操作查找 \(x+1\) ,直至查找不到, 则该数就是答案。 很显然, 以上的操作可以用主席树完成。

code

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

const ll maxn=1000000+10;
struct node
{
    ll lson,rson;
    ll sum;
}tree[maxn<<5];
ll n,tot,rt[maxn];

void change(ll &p,ll l,ll r,ll val)
{
    tree[++tot]=tree[p];
    p=tot;
    tree[p].sum+=val;
    if(l==r) return;
    ll mid=(l+r)>>1;
    if(val<=mid) change(tree[p].lson,l,mid,val);
    else change(tree[p].rson,mid+1,r,val);
}

ll query(ll pl,ll pr,ll l,ll r ,ll val)
{
    if(l==r) return tree[pr].sum-tree[pl].sum;
    ll mid=(l+r)>>1;
    if(val<=mid) return query(tree[pl].lson,tree[pr].lson,l,mid,val);
    else return query(tree[pl].rson,tree[pr].rson,mid+1,r,val)+tree[tree[pr].lson].sum-tree[tree[pl].lson].sum;
}

int main()
{
    ll q;
    cin>>n>>q;
    for(ll i=1;i<=n;i++)
    {
        ll num; cin>>num;
        change(rt[i]=rt[i-1],1,1000000000+10,num);
    }
    ll ans=0,fuck=0;
    for(ll i=0;i<q;i++)
    {
        ll l,r; cin>>l>>r;
        l=(l+ans)%n+1;
        r=(r+ans)%n+1;
        if(l>r) swap(l,r);
        ans=0;
        while(1)
        {
            fuck=query(rt[l-1],rt[r],1,1000000000+10,ans+1);
            if(ans==fuck) break;
            ans=fuck;
        }
        ans++;
        cout<<ans<<"\n";
    }
    return 0;
}

posted @ 2021-04-10 23:25  null4785  阅读(184)  评论(0)    收藏  举报