[学习笔记] 位运算
〇、基础位运算
与运算 / AND
语法:a & b
。
计算方法:按位计算 AND。
运算:1 & 1 = 1, 1 & 0 = 0, 0 & 1 = 0, 0 & 0 = 0
。
或运算 / OR
语法:a | b
。
计算方法:按位计算 OR。
运算:1 | 1 = 1, 1 | 0 = 1, 0 | 1 = 1, 0 | 0 = 0
。
异或运算 / XOR
语法:a ^ b
。
计算方法:按位计算 XOR。
运算:1 ^ 1 = 0, 1 ^ 0 = 1, 0 ^ 1 = 1, 0 ^ 0 = 0
。
取反运算
语法:~a
。
计算方法:将每一位 1 变成 0,0 变成 1。
左移运算
语法:a << b
。
计算方法:\(a \times 2 ^ b \to a\)。
右移运算
语法:a >> b
。
计算方法:\(\displaystyle \left\lfloor\frac{a}{2^b}\right\rfloor \to a\)。
注:>>
是向下取整。
复合赋值运算
语法:a <<= b, a >>= b, a |= b, a &= b, a ^= b
。
小问题
用位运算一定要加括号!!!
用位运算一定要加括号!!!
用位运算一定要加括号!!!
(别问我为什么这么写)
一、常见技巧
二、例题
洛谷 P1225 黑白棋游戏
https://www.luogu.com.cn/problem/P1225
将每个棋盘状压起来,压成一个整数,然后枚举每一次交换,建边,然后跑 Dijkstra。
记得记录一下每个点 \(v\) 的最短路的上一个节点 \(u\),不懂可以见代码,用于输出方案。
哦对了,如果是用这种方式输出方案,最后别忘了先 reverse 一下路径,不然会喜提 66pts。
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
int tmp[4][4], bit[16][2];
char str[10][10];
inline int query () {
int S = 0;
for (int i = 0;i < 16; ++ i) {
int u = bit[i][0], v = bit[i][1];
S |= (1 << i) * tmp[u][v];
}
return S;
}
int head[1 << 16], tot;
struct Edge {
int to, nxt, dis;
} ed[(1 << 21) | (1 << 3)];
inline void add_edge (int u, int v, int w) {
tot ++;
ed[tot].to = v, ed[tot].nxt = head[u], ed[tot].dis = w;
head[u] = tot;
}
int vis[1 << 16], dis[1 << 16], las[1 << 16];
inline void dijkstra (int frm) {
for (int i = 0;i < (1 << 16); ++ i) dis[i] = (1 << 27), vis[i] = 0, las[i] = i;
dis[frm] = 0;
priority_queue < PII, vector < PII >, greater < PII > > heap;
while (!heap.empty ()) heap.pop ();
heap.push (make_pair (0, frm));
while (!heap.empty ()) {
int u = heap.top ().second; heap.pop ();
if (vis[u]) continue; vis[u] = 1;
for (int i = head[u]; i ; i = ed[i].nxt) {
int v = ed[i].to, w = ed[i].dis;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w, las[v] = u;
heap.push (make_pair (dis[v], v));
}
}
}
}
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
int cnt = 0;
for (int i = 0;i < 4; ++ i) {
for (int j = 0;j < 4; ++ j) {
bit[cnt][0] = i, bit[cnt][1] = j, cnt ++;
}
}
for (int S = 0;S < (1 << 16); ++ S) {
int _ = S;
for (int i = 0;i < 4; ++ i) {
for (int j = 0;j < 4; ++ j) {
tmp[i][j] = (_ & 1);
_ >>= 1;
}
}
for (int i = 0;i < 4; ++ i) {
for (int j = 0;j < 4; ++ j) {
if (i != 3) {
if (tmp[i][j] != tmp[i + 1][j]) {
swap (tmp[i][j], tmp[i + 1][j]);
int T = query ();
swap (tmp[i][j], tmp[i + 1][j]);
add_edge (S, T, 1);
}
}
if (j != 3) {
if (tmp[i][j] != tmp[i][j + 1]) {
swap (tmp[i][j], tmp[i][j + 1]);
int T = query ();
swap (tmp[i][j], tmp[i][j + 1]);
add_edge (S, T, 1);
}
}
}
}
}
int S, T;
for (int i = 1;i <= 4; ++ i) scanf ("%s", str[i] + 1);
for (int i = 1;i <= 4; ++ i) {
for (int j = 1;j <= 4; ++ j) {
tmp[i - 1][j - 1] = str[i][j] - '0';
}
}
S = query ();
for (int i = 1;i <= 4; ++ i) scanf ("%s", str[i] + 1);
for (int i = 1;i <= 4; ++ i) {
for (int j = 1;j <= 4; ++ j) {
tmp[i - 1][j - 1] = str[i][j] - '0';
}
}
T = query ();
dijkstra (S);
printf ("%d\n", dis[T]);
vector < int > st; st.clear ();
while (true) {
st.push_back (T);
if (las[T] == T) break;
T = las[T];
}
reverse (st.begin (), st.end ());
for (int i = 0;i + 1 < st.size (); ++ i) {
int fir = st[i], sec = st[i + 1];
int qwq = fir;
for (int x = 0;x < 4; ++ x) {
for (int y = 0;y < 4; ++ y) {
tmp[x][y] = (qwq & 1);
qwq >>= 1;
}
}
for (int x = 0;x < 4; ++ x) {
for (int y = 0;y < 4; ++ y) {
if (x != 3) {
if (tmp[x][y] != tmp[x + 1][y]) {
swap (tmp[x][y], tmp[x + 1][y]);
int awa = query ();
swap (tmp[x][y], tmp[x + 1][y]);
if (awa == sec) {
printf ("%d%d%d%d\n", x + 1, y + 1, x + 2, y + 1);
break;
}
}
}
if (y != 3) {
if (tmp[x][y] != tmp[x][y + 1]) {
swap (tmp[x][y], tmp[x][y + 1]);
int awa = query ();
swap (tmp[x][y], tmp[x][y + 1]);
if (awa == sec) {
printf ("%d%d%d%d\n", x + 1, y + 1, x + 1, y + 2);
}
}
}
}
}
}
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/
CF1800F Dasha and Nightmares
https://codeforces.com/contest/1800/problem/F
首先枚举一下不包含的那个字符。(最多也就 \(26\) 个)
然后对于每一个字符,将不包含它的字符串状压起来 push 到一个 vector 里。
状压方法:如果第 \(i\) 个字符出现次数是奇数就是 \(1\),否则是 \(0\)。
然后统计答案。
对于每一个 vector,用一个数组存一下每一个状态出现次数,然后去一下反(x
取反后就是 x ^ 67108863
,67108863 是 \(2^{26} - 1\),也就是全集,还要去掉不出现的字符,查询一下其出现次数,这样就能达到 \(25\) 个出现过的字符拼接后出现次数u也是奇数)。
冷知识:出现次数为奇数意味着出现次数至少为 \(1\)。
跑的很快,499 ms。
记!得!开! long long!
数!组!不!要!开! long long!
否!则!爆!空!间!
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
bool Memory_Begins;
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
template < class Z >
inline void read (Z &tmp) {
Z x = 0, f = 0;
char c = getchar ();
for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
tmp = !f ? x : -x;
}
vector < int > st[26];
int n;
char s[5000005];
int mp[1 << 26];
bool Memory_Ends;
signed main () {
fprintf (stderr, "%.3lf MB\n", (&Memory_Begins - &Memory_Ends) / 1048576.0);
read (n);
for (int i = 1;i <= n; ++ i) {
scanf ("%s", s + 1);
int m = strlen (s + 1);
for (int qwq = 0;qwq < 26; ++ qwq) {
int S = 0, good = 1;
for (int j = 1;j <= m; ++ j) {
int _ = (1 << (s[j] - 'a'));
S ^= _;
good &= (s[j] - 'a' != qwq);
}
if (good) st[qwq].push_back (S);
}
}
ll ans = 0;
for (int _ = 0;_ < 26; ++ _) {
for (int S : st[_]) mp[S] ++;
for (int S : st[_]) {
int T = S ^ 67108863;
if (T & (1 << _)) T ^= (1 << _);
ans += 1ll * mp[T];
}
for (int S : st[_]) mp[S] --;
}
ans >>= 1;
printf ("%lld\n", ans);
fprintf (stderr, "%.3lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
/*
Things to check:
1. int ? long long ? unsigned int ? unsigned long long ?
2. array size ? is it enough ?
*/