牛客多校第二场g题 transform

思路:  二分

因为对于物品个数打一个前缀和的话,是具有单调性的。 所以可以用二分的思想。在jud函数中借鉴了大佬们的尺取写法。我们可以二分答案,然后jud中枚举左端点,然后可以二分右端点和mid点,可能细节多一些。比较好的一个办法就是尺取的写法。

代码: 

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N =500005;

struct node
{
    ll x;
    ll cnt;
}a[N];

int n;
ll T;
ll prew[N];
ll postw[N];
ll prec[N];
ll postc[N];

bool cmp(node a,node b)
{
    return a.x<b.x;
}

ll dis(int i,int j)
{
    return abs(a[i].x-a[j].x);
}

void init()
{
    for(int i=1;i<=n;i++) prec[i]=prec[i-1]+a[i].cnt;
    for(int i=n;i>=1;i--) postc[i]=postc[i+1]+a[i].cnt;
    for(int i=1;i<=n;i++) prew[i]=prew[i-1]+prec[i-1]*dis(i,i-1)*2;
    for(int i=n;i>=1;i--) postw[i]=postw[i+1]+postc[i+1]*dis(i+1,i)*2;
    /*cout<<endl;
    for(int i=0;i<=n+1;i++) cout<<prec[i]<<" "; cout<<endl;
    for(int i=0;i<=n+1;i++) cout<<prew[i]<<" "; cout<<endl;
    for(int i=0;i<=n+1;i++) cout<<postc[i]<<" "; cout<<endl;
    for(int i=0;i<=n+1;i++) cout<<postw[i]<<" "; cout<<endl;
    */
}

ll cal_pre(int l,int r){
    return prew[r]-prew[l-1]-prec[l-1]*(a[r].x-a[l-1].x)*2;
}
ll cal_suf(int l,int r){
    return postw[l]-postw[r+1]-postc[r+1]*(a[r+1].x-a[l].x)*2;
}

bool jud(ll num){
    ll num2=num/2+1;
    int l=1,r=1,mid=1;
    while(1){
        while(r<=n && prec[r]-prec[l-1]<num) r++;
        while(mid<=n && prec[mid]-prec[l-1]<num2) mid++;
        if(r>n || mid>n) break;
        ll cc=num-(prec[r-1]-prec[l-1]);
        ll s=cal_pre(l,mid)+cal_suf(mid,r-1)+cc*(a[r].x-a[mid].x)*2;
        //   s == 左半边的花费(个数不够num2 +cc == num2 ) +  右半边的花费 + 右半边cc的花费
        if(s<=T) return true;
        l++;
    }
    l=n;r=n;mid=n;
    while(1){
        while(l>=1 && prec[r]-prec[l-1]<num) l--;
        while(mid>=1 && prec[mid]-prec[l-1]<num2) mid--;
        if(l<1 || mid<1) break;
        ll cc=num-(prec[r]-prec[l]);
        ll s=cal_pre(l+1,mid)+cal_suf(mid,r)+cc*(a[mid].x-a[l].x)*2;
        // s== 左半边的花费(个数不够num2 +cc==num2) + 右半边的花费 + 左半边cc的花费
        if(s<=T) return true;
        r--;
    }
    return false;
}


int main()
{
    scanf("%d %lld",&n,&T);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i].x);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i].cnt);

    sort(a+1,a+n+1,cmp);

    init();

    ll l=1,r=prec[n],mid;
    ll ans=-1;
    while(l<=r)
    {
        mid=(l+r)>>1;
        //cout<<"mid "<<mid<<endl;
        if(jud(mid)){
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }

    printf("%lld\n",ans);

    return 0;
}

/*

3 2
1 2 3
2 4 5

*/

猜你喜欢

转载自blog.csdn.net/yjt9299/article/details/81180861