SRM 546 DIV1 Easy - KleofasTail (復習○)

問題


http://community.topcoder.com/stat?c=problem_statement&pm=12049&rd=14738

整数が与えられる。

整数が偶数のときは2で割り、奇数であれば1を引くことができる。

整数の範囲とある数が与えられた時、
その整数の範囲内である数が存在する数が何通りあるか求める。

解き方


数が10^18のため、範囲中の全ての数を全探索で求めることはできない。

そのため、法則がないか数を列挙してみる。
ここで状態遷移図を用いてみる。

1)kが偶数のとき
K←2K←2K+1←4K+2←8K+4
              ←4K+3
    ←4K←8K←16K
          ←8K+1
       ←4K+1←8K+2
 ←K+1←2K+2←4K+4←8K+8
               ←4K+5
          ←2K+3←4K+6→4K+7

2)kが奇数のとき
K←2K←2K+1←4K+2←8K+4
              ←4K+3
    ←4K←8K←16K
          ←8K+1
       ←4K+1←8K+2

つまり、以下の法則が導ける。
kが偶数のときは
 K~K+1,2K~2K+3、4K~4K+7
kが奇数のときは
 K、2K~2K+1、4K~4K+3
最小はKの倍数、最大はKもしくはK+1から始まり2倍+1となる。

コード


class KleofasTail {

public:

long long calc(long long K,long long B){
long long ans=0,low=K,high=K;
if(K%2==0)high++;
while(low<=B){
ans+=min(high,B)-low+1;
low=2*low;
high=2*high+1;
}

return ans;
}

long long countGoodSequences(long long K, long long A, long long B) {
if(K==0)return B-A+1;
return calc(K,B)-calc(K,A-1);
}

};


using namespace std;

#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

class KleofasTail {

public:

long long countGoodSequences(long long K, long long A, long long B) {
if(K==0)return B-A+1;

long long ret=0;
long long e= K%2==1 ? 1 : 2;
long long cur=K;
while(cur<=B){
long long minx=max(cur,A);
long long maxx=min(cur+e-1,B);
ret+=max(0LL,maxx-minx+1);
cur*=2,e*=2;
}

return ret;
}

};
このエントリーをはてなブックマークに追加