位运算的应用与技巧：

&(按位与)、|(按位或)、^(按位异或)、~ (按位取反)。

1、    枚举n个元素的所有子集：

for (int i = 0; i< 1<<n; i++){
for (int j = 0; j < n; j++)
if (i & (1<<j))printf("%d ", j);
}

hdu 4462- Scaringthe Birds

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
int vis[55][55], r[15], x[15], y[15];
int n, k;
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
struct po{
int x, y;
po(int x = 0, int y = 0):x(x), y(y){}
};
vector<po> G[15];

void dfs(int x, int y, int xx, int yy, int r, int c)
{
vis[x][y] = 1;
for (int i = 0; i < 4; i++){
int nx = x + dir[i][0], ny = y + dir[i][1];
if (nx < 1 || nx > n || ny < 1 || ny > n || vis[nx][ny]) continue;
int d = abs(nx-xx) + abs(ny-yy);
if (d <= r){
G[c].push_back(po(nx, ny));
dfs(nx, ny, xx, yy, r, c);
}
}
}

int main()
{
while (scanf("%d", &n), n){
scanf("%d", &k);
for (int i = 1; i <= k; i++) G[i].clear();
for (int i = 1; i <= k; i++) scanf("%d %d", &x[i], &y[i]);
for (int i = 1; i <= k; i++) scanf("%d", &r[i]);
for (int i = 1; i <= k; i++){
memset(vis, 0, sizeof(vis));
dfs(x[i], y[i], x[i], y[i], r[i], i);
}
int a = (1<<k), ans = INF;
if (k == n*n) { puts("0");continue;}
for (int i = 1; i < a; i++){
memset(vis, 0, sizeof(vis));
int cnt = k, c = 0;
for (int j = 1; j <= k; j++) vis[x[j]][y[j]] = 1;
for (int j = 1; j <= k; j++){
if (i & (1 << (j-1))){
c++;
for (int l = 0; l < G[j].size(); l++){
int f = G[j][l].x, s = G[j][l].y;
if (!vis[f][s]){
vis[f][s] = 1;
cnt++;
}
}
}
}
if (cnt == n*n) ans = min(ans, c);
}
if (ans == INF) ans = -1;
printf("%d\n", ans);
}
return 0;
}


2、高效枚举某个集合所有的子集

int cnt = 0;
scanf("%d",&n);
for(; n; n &=(n-1), cnt++);

//如果不考虑空集的话，那么循环到0就可以结束了。

for (int sub = sup;sub; sub = (sub-1) & sup){
//对子集sub的处理
}

int t = sup;
do{
//对子集的处理
t = (t-1) & sup;
}while (t != sup);

Uva1354-MobileComputing

#include<algorithm>
#include<cstdio>
#include<cstring>
#include <vector>
#define INF 0x3f3f3f3f
#define LL long long
#define EPS 1e-6

using namespace std;

const int N = 1<<6;
struct node{
double ll, rl;
node(double l = 0, double r = 0): ll(l), rl(r){}
};
vector<node> scale[N];
int vis[N], w[6], sw[N];
int n;
double r;

int bitcount(int S)
{
int cnt = 0;
for (; S; S &= (S-1), cnt++);
return cnt;
}
void dfs(int S)
{
if (vis[S]) return ;
vis[S] = 1;
if (bitcount(S) == 1){
scale[S].push_back(node(0, 0));
return ;
}
for (int l = S & (S-1), r; l; l = (l-1) & S){
r = S^l;
dfs(l); dfs(r);
for (int i = 0; i < scale[l].size(); i++){
for (int j = 0; j < scale[r].size(); j++){
double llen = max(scale[l][i].ll + sw[r]*1.0/sw[S], scale[r][j].ll - sw[l]*1.0/sw[S]);
double rlen = max(scale[r][j].rl + sw[l]*1.0/sw[S], scale[l][i].rl - sw[r]*1.0/sw[S]);
scale[S].push_back(node(llen, rlen));
}
}
}
}
double solve()
{
int S = (1<<n)-1;
double ans = -1;
dfs(S);
for (int i = 0; i < scale[S].size(); i++){
double t = scale[S][i].ll + scale[S][i].rl;
if (t <= r && t > ans) ans = t;
}
return ans;
}
int main()
{
int t;
scanf("%d", &t);
while (t--){
scanf("%lf %d", &r, &n);
for (int i = 0; i < n; i++) scanf("%d", w+i);
for (int i = 1; i < 1<<n; i++){
vis[i] = sw[i] = 0;
scale[i].clear();
for (int j = 0; j < n; j++)
if (i & (1<<j)) sw[i] += w[j];
}
printf("%.9f\n", solve());
}
return 0;
}


3、枚举大小为k的所有子集。

int t =(1<<k) - 1;
while (t <1<<n){
//对子集的处理
int x = t & -t, y = t + x;
t = ((t & ~y) / x >> 1) |ｙ;
}

Hdu5914- Triangle

#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
#define  INF 0x3f3f3f3f
using namespace std;

int a[25] = {0, 0, 0, 0, 1, 1, 2};

int judge(int x, int n)
{
int b[30];
int k = 0, tag = 1;
for (int i = 1; i <= n; i++)
if (!(x & (1 << (i-1)))) b[k++] = i;
for (int j = k-1; j >= 2 && tag; j--)
if (b[j] < b[j-1]+b[j-2]) tag = 0;
return tag;
}

int main()
{
for (int i = 7; i <= 20; i++){
int flag = 1;
for (int k = a[i-1]; flag ; k++){
int co = (1<<k)-1;
while (co < 1 << i){
int x = co & -co, y = co + x;
co = ((co & ~y) / x >> 1) | y;
if (judge(co, i)) {flag = 0; a[i] = k; break;}
}
}
}
int t, ca = 0, n;
scanf("%d", &t);
while (t--){
scanf("%d", &n);
printf("Case #%d: %d\n", ++ca, a[n]);
}
return 0;
}


4、枚举不相邻元素的集合。

for (int i = 1; i< (1<<n); i++){
if ((i >> 1) & i) continue;
//对子集的处理
}

5、    枚举第k位一定为1的所有子集。

//从满足条件的最小的整数m开始,枚举所有第k位一定为1的所有子集：
for (int t = m; t< (1 << n); t = (t+1) | m){
//对子集的处理
}

6、    当然还有很多有用的位运算，在此借用Matrix67大牛的总结：

7、    关于位运算的一点总结：

/*1、交换两个整数的值：
int a, b;
a = a ^ b;
b = a ^ b;
a = a ^ b;

a = a + b;
b = a - b;
a = a - b;

2、与2的x次方的运算
int a;
a <<= x;//乘2^x
a >= x;//除2^x
a & (1 << x) - 1 //取a%(2^x)的余数
a & 1//判断奇偶性，为0则a为偶数,否则为奇数
a & (a-1)//判断a是否为2的幂或者0，结果为0代表是，否则代表不是

3、其他常用技巧
int a, b;
a = ~a + 1//取相反数
a = (a ^ (a >> 31)) - (a >> 31) //取绝对值
(a & b) + ((a ^ b) >> 1) //取平均值
a ^ b//判断a、b符号是否相同，如果结果>0则相同，否则不同
*/

(1)、位运算优先级较低，如果结合其他的运算符，那么最好在使用时加上括号，当然如果很清楚优先级就另当别论了。
(2)、位运算虽说高效，但是很多枚举子集的技巧数据量大点就无法使用了，所以还是得慎用，根据题目的数据范围而定吧。
(3)、使用位运算注意细节的处理，比如说枚举子集时的起点，终点等等。
(4)、使用位运算关键是理解每一个运算的特点，灵活运用它们的性质，并且找到问题与位之间的联系，其实上面的几个枚举集合的技巧都是根据位之间的联系然后运用相应的运算符得出的，一些位运算符也有一些非常重要的特点，比如说异或运算具有交换律，结合律，自反性等等。
(5)、位运算应用很广，体现在很多算法和数据结构上，比如说状态压缩，树状数组等等，在状态压缩中的使用通常是最常见的，很多算法都设计到状态之间的转移，比如说搜索，dp等等。有时候很难表示当前的这个状态，但是通过二进制位便可以解决了，所以位运算还是很方便和实用的，比如说一些在棋盘，网格中要表示某一行/列的状态时的问题。