0923考试T3 二进制,位运算

0923考试T3

​ 题目描述:

​ 前方的地上散落着B朵樱花,此时刮起了风,便引来一场樱花雨。樱花雨一共持续了N秒。每一秒都会有A朵樱花飘落。小Q细心的记录了每一秒时间后地上樱花的数目,并将其转换成了二进制。小Q想请你统计一下这些二进制数一共有多少个1。

\(2 \le N \le 10 ^ 9, 1 \le A \le 10 ^ 4, 1 \le B \le 10 ^ {16}\)

​ 二进制,位运算。

​ 对于一个等差数列\(b + a, b +2 * a, b + 3 * a,...,b+n * a\), 第\(i\)项与第\(2 ^ k + i\)项的二进制下第\(k\)位是相同的。为啥呢?因为任何一个二进制,两个它相加相当于乘2,相当于左移一位,那那么第一位就为0了(假设没有第0位,从第一位开始编号),所以现在\(k = 1\)\(b\)\(b + 2 ^ 1 * a\)\(1\)位相同。

​ 先介绍一下\(calc\)函数,\(calc(x, t)\)代表对于前\(x\)项,\(2 ^ k = t\),第\(k - 1\)位上有几个1。

​ 假设当前\(k = 3\),如果说第\(i\)项的后\(k - 1\)位小于100,对应这句:\((b + a * i) \% t < (t >> 1)\),那么这一项的第\(k - 1\)为肯定不为1。从\((b + a * i) \% t\)\((t >> 1)\)中间的数肯定第\(k - 1\)位也不为0,那么直接跳就好了,跳的项数就是\(((t >> 1) - (b + a * i) \% t - 1) / a\)。下面$else $同理。

​ 主函数里那个循环:

​ 当\(n >= i\)时,\(i\)的含义有两个:前\(i\)项,\(2^k = i\),枚举到第\(k\)位。此时可以用上面那个定理,因为他有循环节,\(b, b + 2^k, b +2^k + 2 ^ k...\),它们的第\(k\)位都相同。此时我们只需要计算\((n / i) * calc(i, i) + calc(n \% i, i)\)就好了。

​ 当\(n >= i\)时,\(i\)的含义只有\(2^k = i\),枚举到第\(k\)位。此时我们只要算\(calc(n, i)\)就好了。

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

long long a, b, n, T, ans;

long long calc(long long x, long long t) {
    long long res = 0;
    // cout << x << " " << t << endl;
    for(long long i = 1;i <= x; i++) {
        if((b + a * i) % t < (t >> 1)) 
            i = min(x, i + ((t >> 1) - (b + a * i) % t - 1) / a);
        else {
            long long tmp = min(x, i + (t - (b + a * i) % t - 1) / a);
            res += tmp - i + 1; i = tmp;
        }
    }
    return res;
}

int main() { 

    freopen("flowers.in","r",stdin); freopen("flowers.out","w",stdout);

    T = read();
    while(T --> 0) {
        ans = 0;
        a = read(); b = read(); n = read();
        for(long long i = 2;i < ((b + a * n) << 1); i <<= 1) {
            if(n / i) ans += 1ll * (n / i) * calc(i, i);
            ans += calc(n % i, i);
        }
        printf("%lld\n", ans);
    }

    fclose(stdin); fclose(stdout);
    return 0;
}

/*
2
7 4 1
5 8 2

2
2 4 5
5 9 5
*/
posted @ 2020-09-24 21:25  C锥  阅读(155)  评论(0编辑  收藏  举报