牛客小白91 F---位掩码 拆位计算贡献

题目


是一道位掩码的好题,忍不住想写下题解去记录下,首先暴力做法就不提了,我们要做的就是优化,这里使用的是拆位
比如对于

3 2
11
10
那么对于第一个是 1 第二个是2*0 相加为1 你会发现拆位算 
答案是不改变的 此题也是用拆位计算的

直接讲做法 首先外层套个20的循环 里层来个On的循环
然后对于每一个0 1 都需要保存下来 注释都在代码里,可以看懂。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int range = 1e6 + 10;
const int mod = 1e9 + 7;
int n;
int a[range];
int v1, v0;
int ans;
void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int t = 0; t <= 20; t++) {
		//拆位 v1v0 只对本次位 有用
		v1=v0=0;
		for (int i = 1; i <= n; i++) {
			
			if ((a[i] >> t) & 1) {
				ans += ((((v0 * (n - i + 1) % mod) * (1 << t)) % mod) * 2) % mod;
				//在1--i-1的范围内 所有为1  让v0是指为1的包涵这个1的下标为左端点的区间总数
				ans %= mod;
				v1 = (v1 + i) % mod;//加上包涵该左端点的所有可能区间总数 以便后续
			           	//当遍历到0时 拿来给0用 把0当作右端点时候 这个v1就要用上了
			} else {
				ans += ((((v1 * (n - i + 1) % mod) * (1 << t)) % mod) * 2) % mod;			
				ans %= mod;//需要乘2 手写一遍就会发现要*2
				v0 = (v0 + i) % mod;//同上 不过这个0存着后面1用
			}
		}
	}
	cout << ans << endl;


	return ;
}
signed  main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	solve();
	return 0;
}
posted @ 2025-04-16 19:58  LteShuai  阅读(19)  评论(0)    收藏  举报