[学习笔记] 位运算

〇、基础位运算

与运算 / 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

小问题

用位运算一定要加括号!!!

用位运算一定要加括号!!!

用位运算一定要加括号!!!

(别问我为什么这么写)

一、常见技巧

image

image

二、例题

洛谷 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 ?
*/
posted @ 2023-06-16 20:58  CountingGroup  阅读(9)  评论(0编辑  收藏  举报