位运算
基本运算
运算 | C++ 符号 | 意义 |
---|---|---|
and |
& |
按位与 |
or |
` | ` |
not |
~ |
按位取反 |
xor |
^ |
按位异或 |
shl |
<< |
左移 |
shr |
>> |
右移 |
注意运算符运算顺序优先级,可参考 https://www.cnblogs.com/greyqz/p/operator.html。
技♂巧
(源:江苏省淮阴中学薛志坚)
功能 | 示例 | 位运算 |
---|---|---|
去掉最后一位 | (101101 -> 10110) | x >> 1 |
在最后加一个 0 | (101101 -> 1011010) | x << 1 |
在最后加一个 1 | (101101 -> 1011011) | x << 1 + 1 |
把最后一位变成 1 | (101100 -> 101101) | `x |
把最后一位变成 0 | (101101 -> 101100) | `x |
最后一位取反 | (101101 -> 101100) | x ^ 1 |
把右数第 k 位变成1 | (101001 -> 101101, k = 3) | `x |
把右数第 k 位变成0 | (101101 -> 101001, k = 3) | x & !(1 << (k - 1)) |
右数第 k 位取反 | (101001 -> 101101, k = 3) | x ^ (1 << (k - 1)) |
取末 k 位 | (1101101 -> 1101, k = 5) | x & (1 << k - 1) |
取右数第 k 位 | (1101101 -> 1, k = 4) | x << (k - 1) & 1 |
把末 k 位变成 1 | (101001 -> 101111, k = 4) | `x |
末 k 位取反 | (101001 -> 100110,k = 4) | x ^ (1 << k - 1) |
把右边连续的 1 变成 0 | (100101111 -> 100100000) | x & (x + 1) |
把右起第一个 0 变成 1 | (100101111 -> 100111111) | `x |
把右边连续的 0 变成 1 | (11011000 -> 11011111) | `x |
取右边连续的 1 | (100101111 -> 1111) | (x ^ (x + 1)) << 1 |
去掉右起第一个 1 的左边 | (100101000 -> 1000) | x & !(x ^ (x - 1)) (或 x & (-x) ) |
判断一个数 x 是否是 2 的整数次幂:
if (!(x&x-1)) // 法 1
if ((x&-x)==x) // 法 2
Quick swap
a ^= b; b ^= a; a ^= b;
Quick min
inline int min(int a, int b) {
int c = (a - b) >> 31;
return a ^ c | b ^ ~c;
}
lowbit
\[\operatorname{lowbit}(n)=n ~\&~ (\sim n+1)=n~\&~(-n)
\]
popcount
统计 1 的个数:
for (; x; x-=x&-x) ++cnt;
统计 0 的个数:
for (; x; x&=x-1) ++cnt;
统计 32 位无符号整数中 1 的个数:
inline int popcount(unsigned int i) {
i=i-((i>>1)&0x55555555);
i=(i&0x33333333)+((i>>2)&0x33333333);
i=(i+(i>>4))&0x0f0f0f0f;
i=i+(i>>8);
i=i+(i>>16);
return i&0x3f;
}
Quick log (Hash)
令 \(H[2^k \bmod 37]=k\)。因为 \(\forall k\in[0,35], 2^k \bmod 37\) 互不相等,且恰好取遍整数 1~36。
int H[37];
for (int i=0; i<36; ++i) H[(1ll<<i)%37]=i;
Quick Power
#include <cstdio>
#define ll long long
int n, m, mod;
int qpow(int x, int y) {
int res=1%mod;
for (; y; x=(ll)x*x%mod, y>>=1) if (y&1) res=(ll)res*x%mod;
return res;
}
int main() {
scanf("%d%d%d", &n, &m, &mod);
printf("%d\n", qpow(n, m));
return 0;
}
Quick Multiply
#include <cstdio>
#define ll long long
ll n, m, mod;
ll qmul(ll x, ll y) {
ll res=0;
for (; y; x=(x<<1)%mod, y>>=1) if (y&1) res=(res+x)%mod;
return res;
}
int main() {
scanf("%lld%lld%lld", &n, &m, &mod);
printf("%lld\n", qmul(n, m));
return 0;
}
shortest Hamilton path
Hamilton 路径:从 0 到 \(n-1\) 不重不漏地经过每个点恰好一次。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=20;
int n, dist[N][N], f[1<<N][N];
int main() {
scanf("%d", &n);
for (int i=0; i<n; i++) for (int j=0; j<n; j++)
scanf("%d", &dist[i][j]);
memset(f, 0x3f, sizeof f);
f[1][0]=0;
for (int i=1; i<1<<n; i++)
for (int j=0; j<n; j++) if (i>>j&1)
for (int k=0; k<n; k++) if ((i^1<<j)>>k&1)
f[i][j]=min(f[i][j], f[i^1<<j][k]+dist[k][j]);
printf("%d\n", f[(1<<n)-1][n-1]);
return 0;
}
状态压缩动态规划
[SCOI2005] 互不侵犯
在 N×N 的棋盘里面放 K 个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共 8 个格子。
// p1896
#include <cstdio>
#define ll long long
using namespace std;
const int N = 11, M = 2000;
int n, m;
ll ans, dp[N][M][N*N];
int s[M], t[M], cnt;
void dfs(int x, int stat, int p) {
if (x >= n) {
s[++cnt] = stat, t[cnt] = p;
return;
}
dfs(x+1, stat, p);
dfs(x+2, stat|(1<<x), p+1);
}
int main() {
scanf("%d%d", &n, &m);
dfs(0, 0, 0);
for (int i=1; i<=cnt; i++) dp[1][i][t[i]]=1;
for (int i=2; i<=n; i++)
for (int j=1; j<=cnt; j++)
for (int k=1; k<=cnt; k++) {
if (s[j] & s[k]) continue;
if ((s[j]<<1) & s[k]) continue;
if (s[j] & (s[k]<<1)) continue;
for (int l=m; l>=t[j]; l--)
dp[i][j][l] += dp[i-1][k][l-t[j]];
}
for (int i=1; i<=cnt; i++) ans += dp[n][i][m];
printf("%lld\n", ans);
return 0;
}
[USACO06NOV] Corn Fields
农场主 John 新买了一块长方形的新牧场,这块牧场被划分成 M 行 N 列 (1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John 打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 John 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。John 想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
// p1879
#include <cstdio>
using namespace std;
const int N = 4197, M = 15, MOD = 100000000;
int n, m, dp[M][N], ans;
struct node { int s[N], st; } p[M];
int main() {
scanf("%d%d", &m, &n);
for (int i=1, a, t; i<=m; i++) {
t = 0;
for (int j=1; j<=n; j++)
scanf("%d", &a), t = (t<<1) + 1 - a;
for (int j=0; j< (1<<n); j++)
if ((j&(j<<1))||(j&(j>>1))||(j&t)) continue;
else p[i].s[++p[i].st] = j;
}
for (int i=1; i<=p[1].st; i++) dp[1][i] = 1;
for (int i=2; i<=m; i++)
for (int j=1; j<=p[i].st; j++)
for (int k=1; k<=p[i-1].st; k++)
if (!(p[i].s[j] & p[i-1].s[k]))
dp[i][j] += dp[i-1][k];
for (int i=1; i<=p[m].st; i++)
ans = (ans + dp[m][i]) % MOD;
printf("%d\n", ans);
return 0;
}
Post author 作者: Grey
Copyright Notice 版权说明: Except where otherwise noted, all content of this blog is licensed under a CC BY-NC-SA 4.0 International license. 除非另有说明,本博客上的所有文章均受 知识共享署名 - 非商业性使用 - 相同方式共享 4.0 国际许可协议 保护。