[HDU-6304] Chiaki Sequence Revisited

Chiaki Sequence Revisited http://acm.hdu.edu.cn/showproblem.php?pid=6304

an={1anan1+an1an2n=1,2n3 a n = { 1 n = 1 , 2 a n − a n − 1 + a n − 1 − a n − 2 n ≥ 3

i=1naimod1000000007 ∑ i = 1 n a i mod 1000000007

There are multiple test cases. The first line of input contains an integer T(1T105) T ( 1 ≤ T ≤ 10 5 ) , indicating the number of test cases. For each test case:
The first line contains an integer n(1n1018) n ( 1 ≤ n ≤ 10 18 ) .

思路

an a n 序列中某个值 x x 出现的次数是 max{k+1|2kx} max { k + 1 | 2 k 整 除 x }

然后二分得到 an a n

然后可以 logan log ⁡ a n 时间内求出

aianai=2kan[(k+1)gcd(2k,ai)=2kaianai] ∑ a i ≤ a n a i = ∑ 2 k ≤ a n [ ( k + 1 ) ⋅ ∑ g c d ( 2 k , a i ) = 2 k a i ≤ a n a i ]

然后 减掉多加的部分就得到了答案

ans=aianaii>n,aianai a n s = ∑ a i ≤ a n a i − ∑ i > n , a i ≤ a n a i

#pragma GCC optimize ("O2")
#include
using namespace std;

const int MAXN = (int)1e5 + 5;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-8;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int MOD = 1000000007;

ll d[64];

ll f(ll n) {
    ll num = 0;
    for(ll i = 1; d[i] <= n; ++i) {
        num += i * ((n / d[i] + 1) / 2);
    }
    return num + 1;
}

int sum(ll n) {
    ll res = 0;
    for(ll i = 1; d[i] <= n; ++i) {
        //易错
        (res += i * ((d[i] % MOD + d[i] % MOD + (2 * d[i]) % MOD * (((n / d[i] + 1) / 2 - 1) % MOD) % MOD) % MOD * ((n / d[i] + 1) / 2 % MOD) % MOD * 500000004 % MOD) % MOD) %= MOD;
    }
    return (int)res;
}

ll work(ll n) {
    // 一定要加速收敛
    ll low = max(n / 2, 0LL), top = n / 2 + 30;
    while(top - low > 1) {
        ll mid = (low + top) >> 1;
        if(f(mid) >= n) top = mid;
        else low = mid;
    }
    return top;
}

void slove(ll n) {
    ll sop = work(n);
    ll pos = f(sop);
    int ans = sum(sop) + 1;
    ans -= (pos - n) * sop % MOD;
    printf("%d\n", (ans + MOD) % MOD);
}

ll read(ll &x) {
    char c;
    while((c = getchar()) <= 32);
    for(x = 0; c >= '0'; c = getchar()) x = x * 10 + c - '0';
}

int main()
{
    d[1] = 1;
    for(int i = 2; i < 64; ++i) d[i] = d[i - 1] << 1;
    int T;ll n;
    while(scanf("%d", &T) != EOF) {
        while(T--) {
            read(n);
            slove(n);
        }
    }
    return 0;
}

你可能感兴趣的:(二分)