题意:多测,每次给出n和k,n代表长度为n的数组,每个数有二个属性,第一个是本身的值,第二个是这个数是否能被修改,如果能被修改那么就可以让k减去1让数的值增加1,可以从数组里面随意拿出一个数,对于剩下的数求出中位数,问拿出来的数和中位数的和的最大值是多少?
思路:贪心+二分,可以观察到最终的答案构成是拿出的数和中位数组成的,那么就只有二种情况,第一种是让拿出来的数变大,第二种是让中位数变大,不存在都变大的情况,因为花费1代价不一定可以让中位数变大,但是一定可以让拿出来的数变大。
第一种情况,就是当数可以被改变的时候,就去让这个数最大,并且拿出去,然后加上中位数就可以了。可以观察到,如果n是奇数,那么那中位数前面的数,不会改变中位数,拿中位数及其后面的数,会让中位数往前推一个。如果n是偶数,那么那中位数及其前面的数,会让中位数往后推一个,拿中位数及其后面的数,不会改变中位数。
第二种情况,直接将最大的数拿走,然后二分的求出中位数即可。因为如果要改变中位数,假定k无穷,并且所有的数都可以被改变,那么一定会用k来增加中位数及其之后的数,这些数最后一定是相等的,如果不将最大值拿走,那么到达相等的阶段要花费更多的代价。
最后二种答案取最值。
//冷静,冷静,冷静
//调不出来就重构
//#pragma GCC optimize(2)
//#pragma GCC optimize("O3")
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pii;
const int N=1e6+10,mod=1000000007;
struct node
{
ll a,b;
}p[N];
bool cmp(node &a,node &b)
{
return a.a<b.a;
}
ll n;
bool is(ll x,ll k)
{
ll sum=0;
for(int i=n-2;i>=0;i--)
{
if(p[i].a>=x)sum++;
else
{
if(p[i].a+k>=x&&p[i].b)
{
sum++;
k=k-(x-p[i].a);
}
else sum--;
}
}
return sum>0;
}
ll solve(ll k)
{
ll l=0,r=1e9+10;
while(l+1<r)
{
ll mid=l+r>>1;
if(is(mid,k))l=mid;
else r=mid;
}
if(is(r,k))l=r;
return l;
}
void Jiuyuan()
{
ll k;cin>>n>>k;
for(int i=0;i<n;i++)cin>>p[i].a;
for(int i=0;i<n;i++)cin>>p[i].b;
ll ans=0;
sort(p,p+n,cmp);
for(int i=0;i<n;i++)
{
if(p[i].b)
{
if(n&1)
{
if(i<(n-1)/2)ans=max(ans,p[i].a+k+p[(n-1)/2].a);
else ans=max(ans,p[i].a+k+p[(n-1)/2-1].a);
}
else
{
if(i>(n-1)/2)ans=max(ans,p[i].a+k+p[(n-1)/2].a);
else ans=max(ans,p[i].a+k+p[(n-1)/2+1].a);
}
}
}
ans=max(ans,solve(k)+p[n-1].a);
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll T=1;
cin>>T;
while(T--)
{
Jiuyuan();
}
return 0;
}