联合训练赛 E相同的数字

				E	相同的数字

题面
思路:由题意可以发现这个序列可以变成的数字是有一个集合的。
然后那个集合大约10几个元素,他们的共同点时在二进制情况下公共前缀相同,后面加上若干个0.
比如
在这里插入图片描述

第一组数据的公共前缀是1,第二组的是11(10进制下的3).
第一组的可变成的解集是(1,2,4,8,…)
第一组的可变成的解集是(3,6,12,24,…) 通项公式 3*2^x
然后就对哪十几个数暴力一下就行了

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+15;
typedef long long ll;
ll a[N],n;
ll change(ll x,ll y)
{
    ll cnt=0;
    while(y%x!=0)
    {
        x/=2;
        cnt++;
    }
    while(x!=y)
    {
        x*=2;   cnt++;
    }
    return cnt;
}
ll solve(ll x)
{
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans+=change(a[i],x);
    }
    return ans;
}
ll check(ll x)
{
    ll cnt=0;
    for(int i=1;i<=n;i++)
    {
        ll op=a[i];
        while(op>=x)
        {
            if(op==x)
                cnt++;
            op/=2;
        }
    }
    if(cnt==n)
        return 1;
    return 0;
}
int main()
{
    ll minz=112345678912345;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        minz=min(minz,a[i]);
    }
    if(minz==0)
    {
        ll sb=0;
        while(1)
        {
            sb+=1;
        }
    }
    ll x=1,y;
    while(x<=minz*2+1)
    {
        y=x*2;
        if(y>minz)
            break;
        if(check(y))
        {
            x=y;
        }
        else
        {
            y=x*2+1;
            if(y>minz)
                break;
            if(check(y))
            {
                x=y;
            }
            else
            {
                break;
            }
        }
    }
    while(x<=100000000)
        x*=2;
    ll ans=solve(1);
    while(x)
    {
        ans=min(ans,solve(x));
        x/=2;
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43559193/article/details/106842989