Codeforces Round #608 (Div. 2) E. Common Number

题目链接:https://codeforces.com/contest/1271/problem/E

题意:给定函数f(x),x为偶数时f(x)=x/2,x为奇数时f(x)=x-1

给定n,k,对1到n每个数求f(x)的轨迹,如path[15]={15,14,7,6,3,2,1},求在所有轨迹里出现次数大于等于k的最大值ans。

 

以下为轨迹图:n=31时,4在4-5、8-11、16-23中都出现了

n的大小只能控制最后一层的数目

 

 

当图最后一层完整时,每一层的奇数、偶数出现的次数都相等

当图最后一层不完整时,每一层较右(较小)的偶数出现次数总是大于较左的偶数出现次数,奇数同理

所以把奇偶分开来,奇数列和偶数列都呈单调递减态,可以用二分来判断

求出出现次数大于等于k的最大奇数、最大偶数,再求max

 

如何求某个数的出现次数?

4的次数=(5-4+1)+(11-8+1)+(23-16+1),16为不超过n的最大2的次方,23=11*2+1,11=5*2+1

需要注意最后一层不一定完整,23、11等只是个理论值,可能会超过n,当其超过n时要取n

参考自:https://www.cnblogs.com/Herlo/p/12082724.html

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,k;
bool check(ll tmp)
{
    queue<pair<ll,ll> >q;
    if(tmp&1)q.push(make_pair(tmp,tmp));
    else q.push(make_pair(tmp,tmp+1));
    ll ans=0;
    while(q.size())
    {
        auto it=q.front();
        q.pop();
        ans+=min(n,it.second)-it.first+1;
        if((it.first<<1)<=n)q.push(make_pair(it.first<<1,it.second<<1|1));
    }
    return ans>=k;
}
int main()
{
    ll ans=0;
    cin>>n>>k;
    
    ll l=0,r=n/2;
    while(l<=r)
    {
        ll mid=l+r>>1;
        if(check(mid<<1|1))
        {
            ans=max(ans,mid<<1|1);
            l=mid+1;
        }
        else r=mid-1;
    }
    
    l=1,r=n/2;//偶数枚举只能枚因子 
    while(l<=r)
    {
        ll mid=l+r>>1;
        if(check(mid<<1))
        {
            ans=max(ans,mid<<1);
            l=mid+1;
        }
        else r=mid-1;
    }
    cout<<ans<<endl;
    return 0;
} 

 

进阶解法:

尚未参透

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll solve(ll n,ll k)
{
    ll tmp=1;
    while(tmp<=k)tmp*=2;
    tmp/=2;
    //return (n-k+tmp)/tmp; 
    return 1+(n-k)/tmp;
}
int main()
{
    ll n,k,ans;
    cin>>n>>k; 
    ans=max(solve(n,k),solve(n,k+1)<<1);
    cout<<ans<<endl;
    return 0;
} 
posted @ 2020-01-07 17:05  myrtle  阅读(271)  评论(0编辑  收藏  举报