ABC440D
题意:给定一个长度为 n n n 的包含不同整数的数组, q q q 次询问,每次询问不在数组当中出现过的大于等于 x x x 的第 y y y 个数字。
1 ≤ n , q ≤ 3 e 5 1 \leq n,q \leq 3e5 1≤n,q≤3e5
1 ≤ a i , x , y ≤ 1 e 9 1 \leq a_i,x,y \leq 1e9 1≤ai,x,y≤1e9
令 f ( l , r ) f(l,r) f(l,r) 表示区间内不在数组当中出现过的数字个数,易证明,左端点固定时该函数具有单调性。
所以我们可以二分答案的右端点,找到满足 f ( x , t ) ≥ y f(x,t) \geq y f(x,t)≥y 的第一个 t t t 即可。这个个数也可用二分快速求得。
cpp
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,q;
int a[1000005];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
while(q--){
int x,y;cin>>x>>y;
int cnt0=lower_bound(a+1,a+1+n,x)-a-1;
ll l=1,r=2e9+2;
while(l<=r){
ll mid=l+r>>1;
int cnt=mid-x+1-(upper_bound(a+1,a+1+n,mid)-a-1-cnt0);
if(cnt>=y)r=mid-1;
else l=mid+1;
}
cout<<l<<"\n";
}
}
ABC440E
前置知识点:P1631序列合并
给定 n n n 个可以无限选择的数字,任意选择形成恰好为 k k k 的可重集,求出前 x x x 大的集合的和。
1 ≤ n ≤ 50 1 \leq n \leq 50 1≤n≤50
1 ≤ k ≤ 10 5 1 \leq k \leq 10^5 1≤k≤105
1 ≤ x ≤ m i n ( 10 5 , C n + k − 1 k ) 1 \leq x \leq min(10^5,C_{n+k-1}^k) 1≤x≤min(105,Cn+k−1k)
初始最大值一定是所有数字都选最大的。
考虑将一个最大值替换成更小值能形成的所有方案,可以保证次大值一定在这些方案中取到。
由于替换顺序可能会产生影响(如先将一个5替换成3再将一个5替换成4,反过来操作形成的可重集是一样的),我们额外增加一个替换条件,新替换的数字一定比上一次替换的数小。
我们用一个堆维护{当前集合的和,剩余可替换最大值,上一轮替换的数字},这样每次从堆顶取出的集合一定是剩下所有集合当中最大值,同时再取出的集合基础上修改一个最大值最多形成 n n n个不同的更小的集合。
总复杂度为 O ( n x l o g ( n x ) ) O(nxlog(nx)) O(nxlog(nx))
cpp
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,k,x;
ll a[55];
int main(){
cin>>n>>k>>x;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
priority_queue<array<ll,3>>q;
q.push({a[n]*k,k,n-1});
for(int i=1;i<=x;i++){
auto [sum,mx,y]=q.top();
q.pop();
cout<<sum<<"\n";
if(mx){
for(int j=1;j<=y;j++){
q.push({sum-a[n]+a[j],mx-1,j});
}
}
}
}
ABC440F
暂未编辑