Missing Coin Sum Queries题解

2026.9.21,更新解释

Missing Coin Sum Queries

你有 \(n\) 枚正整数值的硬币。这些硬币编号为 \(1,2,…,n\)。

你的任务是处理 \(q\) 个查询,每个查询的形式为:"如果你可以使用硬币 \(a…b\),无法产生的最小和是多少?"

输入

第一行包含两个整数 \(n\) 和 \(q\):硬币的数量和查询的数量。 第二行包含 \(n\) 个整数 \(x1,x2,…,xn\):每枚硬币的价值。 接下来有 \(q\) 行描述查询。每一行包含两个值 \(a\) 和 \(b\):你可以使用硬币 \(a…b\)。

输出

对于每个查询,输出答案。

约束条件

  • \(1≤n,q≤2⋅10^5\)
  • \(1≤x_i≤10^9\)
  • \(1≤a≤b≤n\)

示例

输入:

5 3
2 9 1 2 7
2 4
4 4
1 5

输出:

4
1
6

解释:首先你可以使用硬币 \([9,1,2]\),然后使用硬币 \([2]\),最后使用硬币 \([2,9,1,2,7]\)。

设p为指向当前无法到达的最小值的指针,初始为1

  1. 如果有面值为\(1\)的硬币,那么\(p\)变成\(2\),否则答案为\(1\)
  2. 如果有面值为\(2\)的硬币,那么\(p\)变成\(4\)
  3. ...
  4. 以此类推,如果此时\(p\)为\(n\),且有一枚面值不大于\(n\)的硬币k,那么\(p\)此时变成\(n+k\)
    此时我们就可以写出暴力了
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define be begin()
#define en end()
using namespace std;
const int inf=1e18;
const int mod=1e9+7;
vector<int> q;
void solve(int a,int b)
{
    int p=1;
    vector<int> c;
    for(int i=a;i<=b;i++) c.push_back(q[i]);
    sort(c.be,c.en);
    for(auto i:c)
    {
        if(i<=p)
        {
            p+=i;
        }
        else break;
    }
    cout<<p<<"\n";
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n,m;
    cin>>n>>m;
    q.resize(n+1);
    for(int i=1;i<=n;i++) cin>>q[i];
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        solve(a,b);
    }
    return 0;
}

因为\(p\)为\(n+1\)的意思是 \([1,n]\)以内的硬币都可以得到,那么加上一枚面值为\(k\)的硬币,\([1,n+k]\)内的硬币都可以得到了

逻辑:

  • 初始化\(p=1\),表示当前假设最小无法构造的和为 \(1\);
  • 迭代查询区间内所有价值\(≤p\)的硬币的总价值\(s\);
  • 若\(s < p\),说明无法构造\(p\),直接输出\(p\);
  • 否则更新\(p = s + 1\),继续迭代。

简单来说:
若当前可构造的所有和为\([1, S]\),当新加入的硬币价值\(c \leq S+1\)时,可构造的和范围扩展为\([1, S+c]\);若新加入的硬币价值\(c > S+1\),则\(S+1\)就是无法构造的最小和。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define be begin()
#define en end()
using namespace std;
const int inf=1e18;
const int mod=1e9+7;
vector<int> q,vals,root,lc,rc,sm;
int tot,M;
inline int update(int pre,int l,int r,int pos,int val)
{
    int cur=++tot;
    lc.push_back(lc[pre]);
    rc.push_back(rc[pre]);
    sm.push_back(sm[pre]+val);
    if(l!=r)
    {
        int mid=(l+r)>>1;
        if(pos<=mid) lc[cur]=update(lc[pre],l,mid,pos,val);
        else rc[cur]=update(rc[pre],mid+1,r,pos,val);
    }
    return cur;
}
inline int query(int u,int v,int l,int r,int k)
{
    if(k<=0) return 0;
    if(r<=k) return sm[u]-sm[v];
    int mid=(l+r)>>1;
    int res=query(lc[u],lc[v],l,mid,k);
    if(k>mid)res+=query(rc[u],rc[v],mid+1,r,k);
    return res;
}
void solve(int a,int b)
{
    int p=1;
    while(1)
    {
        int k=upper_bound(vals.begin(),vals.end(),p)-vals.begin();
        //用upper_bound在离散化的vals数组里找到第一个大于p的元素的下标,
        //这个下标-1就是所有面值≤p的硬币在离散化数组里的最大位置,也就是我们要查询的k
        int s=query(root[b],root[a-1],1,M,k);
        if(s<p)
        {
            cout<<p<<"\n";
            return;
        }
        p=s+1;
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n,m;
    cin>>n>>m;
    q.resize(n+1);
    for(int i=1;i<=n;i++)cin>>q[i];
    for(int i=1;i<=n;i++) vals.push_back(q[i]);
    sort(vals.begin(),vals.end());
    vals.erase(unique(vals.begin(),vals.end()),vals.end());
    M=vals.size();
    lc.reserve(n*20+5);
    rc.reserve(n*20+5);
    sm.reserve(n*20+5);
    lc.push_back(0);
    rc.push_back(0);
    sm.push_back(0);
    root.resize(n+1);
    tot=0;
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(vals.begin(),vals.end(),q[i])-vals.begin()+1;
        root[i]=update(root[i-1],1,M,pos,q[i]);
    }
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        solve(a,b);
    }
    return 0;
}
posted @ 2026-09-20 14:51  Aries2011  阅读(30)  评论(0)    收藏  举报