位运算
拆位贡献
题目链接:https://ac.nowcoder.com/acm/contest/22754/H
题意
给定一个长度为n的正整数数组,计算
数据范围
▶ 题解
对于数字间的异或值的和,显然可以拆位对每个位进行单独的考虑;两个数之间异或,如果可以对答案产生贡献,那么两个位必然不相等,即而本题是平方形式的,显然不能直接用数值的平方来计算,因为位是独立存在的,应该将平方的两个式子都进行枚举讨论,即
由上式可知,只有当
因此,设dp[i][j][x][y]表示二进制的第i位为x,第j位为y的数量,预处理过后外面枚举a[i]和a[j]的位数,内层只需要枚举各个a[i],通过dp数组就能找到符合条件的数量,最终以O(
▶ Code:
#include#define int long long #define endl "\n" #define INF 1e15 using namespace std; typedef pair pii; int dp[35][35][3][3]; //dp[i][j][x][y]表示二进制的第i位为x,第j位为y的数量 const int p=1e9+7; signed main(){ std::ios::sync_with_stdio(false);cin.tie(0); int n;cin >> n; vector< int>a(n+1); for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 0; i <= 31; i++) for(int j = 0; j <= 31; j++) for(int k = 1; k <= n; k++) dp[i][j][(a[k] >> i) & 1][(a[k] >> j)& 1]++; int ans = 0; for(int i = 0; i <= 31; i++) for(int j = 0; j <= 31 ;j++) for(int k = 1; k <= n; k++) ans+=((1LL << (i+j)) %p * dp[i][j][((a[k]>>i) &1) ^ 1][((a[k] >> j) &1 ) ^1] %p ) %p; cout << ans << endl; return 0; }
题目链接:https://ac.nowcoder.com/acm/contest/86639/D
题意
有
数据范围
……
数据结构+位运算
线段树
题目链接:https://codeforces.com/contest/242/problem/E
题意
有一个长度为n的数组a,可以对数组执行两个操作:
- 1 l r:查询区间[l,r]的总和
- 2 l r x:对区间[l,r]每个数与x异或,即
对原数组执行m次操作,输出每次查询的结果.
数据范围
▶ 题解
由题意得,修改操作是位运算,所以我们拆开对每个位单独考虑;又因为a[i]的范围不超过1e6,所以只需要进行20位的讨论即可,我们通过二维的结构体建立约20课线段树,tree[i][x]表示从右往左第i位的线段树数组.对于异或操作,我们只需要对输入的x进行拆位,如果第i位为1,则区间内每个数的第i位都要进行取反操作,反之则没有任何影响;所以这题就演变成了线段树区间取反的模板题!其中,结构体变量ans表示该位上区间二进制值的和,size表示区间长度,lazy则表示该区间是否需要取反操作.
对于区间查询总和的操作,因为线段树维护的信息是一个位上的区间"1"和"0"的总和需要将每一位查询到的区间二进制和乘以位的权值,即
▶ Code:
#include#define ll long long #define endl '\n' #define INF 1e15 using namespace std; typedef pair pii; struct segment_tree{ int L, R ,size; int lazy, ans; } tree[21][400010]; int a[100010]; signed main(){ std::ios::sync_with_stdio(false); cin.tie(0); int n, m; cin >> n; for (int i = 1; i <= n;i++) cin >> a[i]; auto up = [&](int x, int idx) -> void { tree[idx][x].ans = tree[idx][x << 1].ans + tree[idx][(x << 1) + 1].ans; }; auto down = [&](int x, int idx) -> void { if (!tree[idx][x].lazy) return; tree[idx][x].ans = tree[idx][x].size - tree[idx][x].ans; if (tree[idx][x].L!=tree[idx][x].R){ tree[idx][x << 1].lazy ^= 1; tree[idx][(x << 1) + 1].lazy ^= 1; } tree[idx][x].lazy = 0; }; auto build = [&](auto bulid, int x, int L, int R, int idx) -> void { tree[idx][x].L = L, tree[idx][x].R = R, tree[idx][x].size = R - L + 1; if (L == R) { tree[idx][x].ans = ((a[L] >> idx) & 1); return; } const int mid = L + R >> 1; bulid(bulid, x << 1, L, mid, idx); bulid(bulid, (x << 1) + 1, mid + 1, R, idx); up(x, idx); }; auto change = [&](auto change, int x, int L, int R, int idx) -> void { down(x, idx); if (tree[idx][x].L > R || tree[idx][x].R < L) return; if (tree[idx][x].L >= L && tree[idx][x].R <= R) { tree[idx][x].lazy ^= 1; down(x, idx); return; } change(change, x << 1, L, R, idx); change(change, (x << 1) + 1, L, R, idx); up(x, idx); }; auto query = [&](auto query, int x, int L, int R, int idx) -> int { down(x, idx); if (tree[idx][x].L > R || tree[idx][x].R < L) return 0; if (tree[idx][x].L >= L && tree[idx][x].R <= R) return tree[idx][x].ans; return query(query, x << 1, L, R, idx) + query(query, (x << 1) + 1, L, R, idx); }; for (int i = 0; i <= 20;i++) build(build, 1, 1, n, i); cin >> m; while (m--){ int opt, L, R; cin >> opt >> L >> R; if (opt == 1){ ll sum = 0; for (int i = 0; i <= 20;i++) sum += (1LL << i) * query(query, 1, L, R, i); cout << sum << endl; } else{ int x; cin >> x; for (int i = 0; i <= 20;i++) if ((x >> i) & 1) change(change, 1, L, R, i); } } return 0; }
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.


