CF2132E题解
赛时卡 D 结果 E 赛后五分钟才调完,非常后悔。
思路
我们先考虑若没有 \(x\) 和 \(y\) 的限制时怎么做。
显然是将 \(a\) 数组和 \(b\) 数组组合在一个长度为 \(n+m\) 的数组 \(c\) 中,然后对 \(c\) 排序并求排序后的前缀和,最后询问时直接输出前 \(z\) 位的前缀和即可。
我们考虑 \(x\) 和 \(y\) 的限制对上面这个做法的限制在哪里。
拿 \(a\) 举例,若我们对 \(c\) 的每个元素标记一下,若原来属于 \(a\) 数组就标记为 \(1\) 否则标记 \(0\),这一步可以对两个数组排序后双指针插入 \(c\) 来做到。记这个标记数组为 \(pre1\),求其前缀和后,\(pre1_i\) 即为 \(c\) 数组前 \(i\) 位来自数组 \(a\) 的元素的个数,显然可以对这个数组二分查找最后一个等于 \(z\) 的位置,即为简单版本中的解法在只有 \(x\) 限制下能取到的最大编号,设这个编号为 \(idx_1\)。
同理对 \(b\) 做对称的处理,得编号 \(idx_2\),两编号的最小值即为最严格的限制,若 \(z\leq \min(idx_1,idx_2)\) 则可以直接取 \(c\) 的前 \(z\) 位前缀和;否则取 \(c\) 的前 \(\min(idx_1,idx_2)\) 位前缀和(因为 \(a,b\) 混搭只能取到这),剩下的 \(z-\min(idx_1,idx_2)\) 个数则用还没到限制的那个数组取值,即再分别对 \(a\) 和 \(b\) 维护前缀和 \(sum1\) 和 \(sum2\),然后就做完了。
code
#include <bits/stdc++.h>
#define int int64_t
//#define int __int128
//#define MOD (17)
//#define eps (1e-6)
#define endl '\n'
#define debug_endl cout<<endl;
#define debug cout<<"debug"<<endl;
using namespace std;
int t,n,m,q;
int a[200010],b[200010],c[400010],pre1[400010],pre2[400010],sum[400010],sum1[200010],sum2[200010];
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>t;
while(t--){
cin>>n>>m>>q;
for(int i=1;i<=n+m;++i){
pre1[i]=pre2[i]=sum[i]=0;
}
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=1;i<=m;++i){
cin>>b[i];
}
sort(a+1,a+n+1,greater<int>());//ab升序排序以双指针
sort(b+1,b+m+1,greater<int>());
for(int i=1;i<=n;++i){
sum1[i]=sum1[i-1]+a[i];
}
for(int i=1;i<=m;++i){
sum2[i]=sum2[i-1]+b[i];
}
int ia=1,ib=1,idx=0;
while(ia<=n&&ib<=m){//双指针将ab插入c中,并标记pre1和pre2
if(a[ia]>b[ib]){
c[++idx]=a[ia];
pre1[idx]=1;
++ia;
}
else{
c[++idx]=b[ib];
pre2[idx]=1;
++ib;
}
}
for(;ia<=n;++ia){//把剩下的a或b插入
c[++idx]=a[ia];
pre1[idx]=1;
}
for(;ib<=m;++ib){
c[++idx]=b[ib];
pre2[idx]=1;
}
for(int i=1;i<=n+m;++i){
pre1[i]+=pre1[i-1];
pre2[i]+=pre2[i-1];
sum[i]=sum[i-1]+c[i];
}
for(int i=1;i<=q;++i){
int x,y,z;
cin>>x>>y>>z;
int idx1=upper_bound(pre1+1,pre1+n+m+1,x)-pre1-1;//找到最后一个等于限制x的位置
int idx2=upper_bound(pre2+1,pre2+n+m+1,y)-pre2-1;
if(z<=min(idx1,idx2)){
cout<<sum[z]<<endl;
}
else if(idx1<idx2){//a数组先到达限制
int res=0;
res+=sum[idx1];
z-=idx1;
res+=sum2[z+pre2[idx1]]-sum2[pre2[idx1]];//只能b中取值了
cout<<res<<endl;
}
else{
int res=0;
res+=sum[idx2];
z-=idx2;
res+=sum1[z+pre1[idx2]]-sum1[pre1[idx2]];
cout<<res<<endl;
}
}
}
return 0;
}

浙公网安备 33010602011771号