AtCoder Regular Contest 158题解(A-C)

文章提供了三个编程问题的解决方案,涉及数值操作的优化。问题A关注通过特定操作使三个数相等,关键在于数的奇偶性和和的倍数性质;问题B讨论找到数对的最大和最小乘积比,关键在于排序和选择;问题C探讨两数之和的所有数字之和,重点在于进位计算。每个问题的解答都包含数学分析和高效的代码实现。
摘要由CSDN通过智能技术生成

A - +3 +5 +7

思路:首先我们可以发现这个问题的关键是三个数之间的差,那么对于+3+5+7这个操作可以等价于-2+0+2这个操作,这样便于解题。那么这样的话其实三个数的和是没有变化的,那么要使三个数最后相等那么三个数的和必然是3的倍数。其次-2+0+2这个操作并没有改变三个数的奇偶性,所以所给三个数的奇偶性要是相同的。

然后我们考虑如何得到答案,在以上的条件限制下是必定有解的那么每次操作-2+0+2就会使|x1-ave|+|x2-ave|+|x3-ave|(ave=(x1+x2+x3)/3)减少4,那么最终的答案就是(|x1-ave|+|x2-ave|+|x3-ave|)/4。

#include <bits/stdc++.h>
#define ll long long
#define db double
#define pii pair<int,int>
#define de cout<<"---"<<endl;
#define mem(x,v) memset(x,v,sizeof(x))
#define L(x) x&(-x)
#define pb push_back//emplace_back//priority_queue <int,vector<int>,greater<int> > q;
#define INF 0x3f3f3f3f
#define endl '\n'
//function<void(int)> dfs = [&](int u);
//#define x first
//#define y second
using namespace std;
const int mod=998244353;
int n;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
ll a[3];
void solve(){
    for(int i=0;i<3;i++)cin>>a[i];
    if((a[0]+a[1]+a[2])%3){
        cout<<"-1";
    }
    else if((a[0]&1)==(a[1]&1)&&(a[0]&1)==(a[2]&1)){
        ll ave=(a[0]+a[1]+a[2])/3;
        ll res=0;
        for(int i=0;i<3;i++){
            if(a[i]<ave)res+=ave-a[i];
            else res-=ave-a[i];
        }
        cout<<res/4;
    }
    else cout<<"-1";
    cout<<endl;
    return ;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int TT=1;
    cin>>TT;
    while(TT--){
        solve();
    }
    return 0;
}

B - Sum-Product Ratio

思路:对于这个问题我们先假设xi和xj已经被选,这样a=xi+xj和b=xi*xj为定值,那么我们发现(xi+xj+xk)/(xi*xj*xk)=(a+xk)(b*xk)=a/b*(1/xk)+1/b,会发现当1/xk取最小值或者最大值时该式子取最大或最小值。那么我们推广到三个数这三个数一定是最大和最小中的三个数,所以按1/x将数组排序,取最小的三个和最大的三个中的三个数构成(xi+xj+xk)/(xi*xj*xk),那么答案必定在这其中。

#include <bits/stdc++.h>
#define ll long long
#define db double
#define pii pair<int,int>
#define de cout<<"---"<<endl;
#define mem(x,v) memset(x,v,sizeof(x))
#define L(x) x&(-x)
#define pb push_back//emplace_back//priority_queue <int,vector<int>,greater<int> > q;
#define INF 0x3f3f3f3f
#define endl '\n'
//function<void(int)> dfs = [&](int u);
//#define x first
//#define y second
using namespace std;
const int mod=998244353;
int n;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
void solve(){
    cout<<fixed<<setprecision(15);
    cin>>n;
    vector<int>a(n);
    for(int i=0;i<n;i++)cin>>a[i];
    vector<int>b;
    sort(a.begin(),a.end(),[&](int x,int y){
        double xx=1.0/x,yy=1.0/y;
        return xx<yy;    
    });
    for(int i=0;i<3;i++){
        b.pb(a[i]);
    }
    for(int i=max(3,n-3);i<n;i++){
        b.pb(a[i]);
    }
    double mi=1e9,ma=-1e9;
    for(int i=0;i<b.size();i++){
        for(int j=i+1;j<b.size();j++){
            for(int k=j+1;k<b.size();k++){
                mi=min(mi,1.0*(b[i]+b[j]+b[k])/b[i]/b[j]/b[k]);
                ma=max(ma,1.0*(b[i]+b[j]+b[k])/b[i]/b[j]/b[k]);
            }
        }
    }
    cout<<mi<<endl;
    cout<<ma<<endl;
    return ;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int TT=1;
    while(TT--){
        solve();
    }
    return 0;
}

C - All Pair Digit Sums

思路:首先对于f(a+b),如果a+b中没有发生进位那么显然f(a+b)=f(a)+f(b)。如果发生了进位的话其实对于f(a+b)来说就是有10进位变成1,那么答案将减小9,那么如果发生k次进位,那么f(a+b)=f(a)+f(b)-k*9。所以我们考虑计算出所有ai+aj产生的进位次数t,再用所有f(xi)+f(xj)的和减去9*t即可。

如何考虑计算进位次数呢,我们可以从每一位上来考虑,对于第x低位来说,如果两个数模上10的x+1次方相加超过10的x+1次方那么就是产生了一次进位。那么我们可以根据数模上10的x+1的次方排序,然后用二分的方法找到每一个数有多少个数加起来使其大于10的x+1次方,然后将这个操作在15位上都做一次,就能得到答案了。

#include <bits/stdc++.h>
#define ll long long
#define db double
#define pii pair<int,int>
#define de cout<<"---"<<endl;
#define mem(x,v) memset(x,v,sizeof(x))
#define L(x) x&(-x)
#define pb push_back//emplace_back//priority_queue <int,vector<int>,greater<int> > q;
#define INF 0x3f3f3f3f
#define endl '\n'
//function<void(int)> dfs = [&](int u);
//#define x first
//#define y second
using namespace std;
const int mod=998244353;
int n;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
void solve(){
    cin>>n;
    vector<ll>a(n);
    ll res=0;
    for(int i=0;i<n;i++){
        cin>>a[i];
        for(ll j=10;j<=1000000000000000;j*=10){
            res+=a[i]%j/(j/10)*2*n;
        }
    }
    for(ll i=0,j=10;i<15;i++,j*=10){
        sort(a.begin(),a.end(),[&](ll x,ll y){
            return x%j<y%j;    
        });
        for(int k=0;k<n;k++){
            int l=0,r=n;
            while(l<r){
                int mid=(l+r)/2;
                if(a[mid]%j<j-a[k]%j){
                    l=mid+1;
                }
                else{
                    r=mid;
                }
            }
            int t=n-l;
            res-=t*9;
        }
    }
    cout<<res<<endl;
    return ;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int TT=1;
    while(TT--){
        solve();
    }
    return 0;
}

O(N*15)做法:基数排序+双指针

#include <bits/stdc++.h>
#define ll long long
#define db double
#define pii pair<int,int>
#define de cout<<"---"<<endl;
#define mem(x,v) memset(x,v,sizeof(x))
#define L(x) x&(-x)
#define pb push_back//emplace_back//priority_queue <int,vector<int>,greater<int> > q;
#define INF 0x3f3f3f3f
#define endl '\n'
//function<void(int)> dfs = [&](int u);
//#define x first
//#define y second
using namespace std;
const int mod=998244353;
int n;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
void solve(){
    cin>>n;
    vector<ll>a(n);
    ll res=0;
    for(int i=0;i<n;i++){
        cin>>a[i];
        for(ll j=10;j<=1000000000000000;j*=10){
            res+=a[i]%j/(j/10)*2*n;
        }
    }
    vector<ll>b(n);
    for(ll i=0,j=10;i<15;i++,j*=10){
        vector<ll>c[10];
        for(int k=0;k<n;k++){
            c[a[k]%j/(j/10)].pb(a[k]);
        }
        int t=0;
        for(int k=0;k<10;k++){
            for(auto kk:c[k]){
                b[t++]=kk;
            }
        }
        a=b;
        int kk=n-1;
        for(int k=0;k<n;k++){
            while(kk>=0&&a[kk]%j+a[k]%j>=j){
                kk--;
            }
            int t=n-kk-1;
            res-=t*9;
        }
    }
    cout<<res<<endl;
    return ;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int TT=1;
    while(TT--){
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值