牛客周赛 Round 9【题解完成】

123 阅读3分钟
题目难度知识点
A 小美的外卖订单编号签到
B 小美的加法前缀和
C 小美的01串翻转枚举+前缀和
D 小美的数组操作★★思维

image.png D题属于思维题吧,确实想不到能想一点但是就是想不到后面。

小美的外卖订单编号

#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e5*5+10;
int main(void)
{
    int t; cin>>t;
    while(t--)
    {
        int m,x; cin>>m>>x;
        cout<<(x-1)%m+1<<'\n';
    }
    return 0;
}

小美的加法

#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e5*5+10;
LL a[N],n,s[N];
int main(void)
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[i];
    LL ans=s[n];
    for(int i=1;i<=n-1;i++)
    {
        LL l=i,r=i+1;
        ans=max(ans,s[n]-a[l]-a[r]+a[l]*a[r]);
    }
    cout<<ans<<endl;
    return 0;
}

小美的01串翻转

#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e5*5+10;
string a,b,c;
LL cnt1[N],cnt2[N];
int main(void)
{
    cin>>a;
    int n=a.size();
    while(b.size()<a.size()) b+="01";
    while(c.size()<a.size()) c+="10";
    a="."+a,b="."+b,c="."+c;
    for(int i=1;i<=a.size();i++)
    {
        cnt1[i]=cnt1[i-1],cnt2[i]=cnt2[i-1];
        if(b[i]!=a[i]) cnt1[i]++;
        if(c[i]!=a[i]) cnt2[i]++;
    }
    LL ans=0;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            int l=j,r=j+i-1;
            if(r>n) break;
            ans+=min(cnt1[r]-cnt1[l-1],cnt2[r]-cnt2[l-1]);
        }
    }
    cout<<ans;
    return 0;
}

小美的数组操作

image.png

image.png

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
typedef long long int LL;
LL a[N],n,sum;
LL f(LL x,LL l,LL r)
{
    LL sum1=0,sum2=0;
    for(int i=l;i<=r;i++) 
    {
        sum1+=max(a[i]-x,0ll);//大于x的部分
        sum2+=max(x-a[i],0ll);//小于x的部分
    }
    return min(sum1,sum2)+max(sum1,sum2)-min(sum1,sum2);
    //return max(sum1,sum2);
}
LL solve(int x,int y)
{
     LL l=a[x],r=a[y];//二分出可能的众数,因为这是一个u型的曲线,存在极小值点。
     while(l<r)
     {
        LL mid=(l+r+1)/2;
        if(f(mid,x,y)<=f(mid-1,x,y)) l=mid;
        else r=mid-1;
    }
    LL ans=f(l,x,y);
    for(int i=-10;i<=10;i++) ans=min(ans,f(i+l,x,y));
    return ans;
}
int main(void)
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];
    if(sum%n==0)
    {
        LL ans=0;
        for(int i=1;i<=n;i++) ans+=max(0ll,a[i]-sum/n);//只算大于平均数的就行,因为当大于的计算好了对称的小于的自动就好了故只用算一个就行了。
        cout<<ans;
    }else
    {
        sort(a+1,a+n+1);
        cout<<min(solve(1,n-1),solve(2,n));//去掉最小值或者最大值,让去掉的数当一个备用的用于调整n-1个数相等的备胎。为啥去掉这俩数是因为,我们最终的结果肯定是趋于平均数的,第一个数和最后一个数显然是离的最远的点,根据贪心是不优的。
    }
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*5+10;
typedef long long int LL;
LL a[N],n,sum;
LL f(LL x,LL l,LL r)
{
    LL sum1=0,sum2=0;
    for(int i=l;i<=r;i++) 
    {
        sum1+=max(a[i]-x,0ll);//大于x的部分
        sum2+=max(x-a[i],0ll);//小于x的部分
    }
    return min(sum1,sum2)+max(sum1,sum2)-min(sum1,sum2);//公共部分+多余的需要用备胎要调整的部分。
    //return max(sum1,sum2);
}
LL solve(int x,int y,LL sum)
{
    LL temp=sum/(n-1);//不需要二分也行,因为最优的结果一定是平均数的附近,暴力的找平均数附近的小区间就行了。
    LL ans=f(temp,x,y);
    for(int i=-10;i<=10;i++) ans=min(ans,f(i+temp,x,y));
    return ans;
}
int main(void)
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];
    if(sum%n==0)
    {
        LL ans=0;
        for(int i=1;i<=n;i++) ans+=max(0ll,a[i]-sum/n);
        cout<<ans;
    }else
    {
        sort(a+1,a+n+1);
        cout<<min(solve(1,n-1,sum-a[n]),solve(2,n,sum-a[1]));
    }
    return 0;
}