ABC283Ex Popcount Sum 题解

Problem

\(\text{popcount}(n)\) 表示 \(n\) 在二进制表示中 \(1\) 的数量。

给定 \(n, m, r\),请求出 \(\sum\limits_{i = 1}^{n} [i\bmod m = r]\times\text{popcount}(i)\)

多组数据,\(1\le T \le 10^5,\ 1\le m \le n \le 10^9,\ 0\le r < m\)

时空限制:4s, 1GB。

Sol

发现这个 \(\text{popcount}\) 和取模没有什么关系,不好直接处理,容易想到拆位处理:\(\sum\limits_{i\bmod m = r}^{n}\sum\limits_{j = 0}^{30} [\frac{i}{2 ^ j}\bmod 2 \equiv 1]\)。然后把在求和符号中的判断变一下,即用 \(km + r\) 表示 \(i\),并交换 \(i, j\) 两维:\(\sum\limits_{j = 0}^{30}\sum\limits_{i = 0}^{\lfloor\frac{n - r}{m}\rfloor}[\lfloor\frac{im + r}{2 ^ j}\rfloor\bmod 2 \equiv 1]\)

接着就是一个很正常的 trick 了,将取模运算用除法表示,变为:\(\sum\limits_{j = 0}^{30}\sum\limits_{i = 0}^{\lfloor\frac{n - r}{m}\rfloor}[\lfloor\frac{im + r}{2 ^ j}\rfloor - 2\times\lfloor\frac{\lfloor\frac{im + r}{2 ^ j}\rfloor}{2}\rfloor = 1]\),然后 \(\lfloor\frac{im + r}{2 ^ j}\rfloor\) 肯定是 \(\ge 2\times\lfloor\frac{\lfloor\frac{im + r}{2 ^ j}\rfloor}{2}\rfloor\) 的,于是可以直接变为相减。然后再根据向下整除的性质可以发现 \(\lfloor\frac{\lfloor\frac{im + r}{2 ^ j}\rfloor}{2}\rfloor\) 的下面部分可以直接相乘。原式就可以化为:\(\sum\limits_{j = 0}^{30}\sum\limits_{i = 0}^{\lfloor\frac{n - r}{m}\rfloor}\lfloor\frac{im + r}{2 ^ j}\rfloor - 2\times\lfloor\frac{im + r}{2 ^ {j + 1}}\rfloor\),这就很简单了,前后分别类欧计算即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define sz(a) ((int) (a).size())
#define vi vector < int >
#define pb emplace_back
using namespace std;
ll F(ll a, ll b, ll c, ll n) {
	if(!a)
		return b / c * (n + 1);
	if(a >= c || b >= c)
		return n * (n + 1) / 2 * (a / c) + (b / c) * (n + 1) + F(a % c, b % c, c, n);
	ll m = (a * n + b) / c;
	return m * n - F(c, c - b - 1, a, m - 1);
}
int n, m, r;
void solve() {
	cin >> n >> m >> r;
	ll ans = 0;
	int lim = (n - r) / m;
	for(int i = 0; i <= 30; ++i)
		ans += F(m, r, 1 << i, lim) - 2 * F(m, r, 1ll << (i + 1), lim);
	cout << ans << "\n";
}
int main() {
	ios :: sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}
posted @ 2024-02-17 22:39  Pengzt  阅读(14)  评论(0编辑  收藏  举报