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
*/