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\)的硬币,那么\(p\)变成\(2\),否则答案为\(1\)
- 如果有面值为\(2\)的硬币,那么\(p\)变成\(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;
}
本文来自博客园,作者:Aries2011,转载请注明原文链接:https://www.cnblogs.com/Aries2011/p/23048620

浙公网安备 33010602011771号