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
", ans);
    }

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

/*
2
7 4 1
5 8 2

2
2 4 5
5 9 5
*/
原文地址:https://www.cnblogs.com/czhui666/p/13726765.html