Codeforces Round #150 (Div. 2) C 位运算+思维
http://codeforces.com/contest/244
题意:
给定n个数的序列,n (1 ≤ n ≤ 105) 定义f[l,r] = a[l]|a[l + 1]|......|a[r]求该序列所有所有的不同的f[i,j]的值的个数;
思路:
这题当时看错了写了个n*logn(n)的算法自以为很好结果一提交WA。很明显算法错了。后来怎么想也没想出什么优化来。思维啊.. 纯暴力是(n^3)
我们可以通过这个循环来优化到O(n^2) f[j]表示j到i这一段取或操作得到的数
for (i = 0; i < n; ++i){ for (j = 0; j < i; ++j){ f[j] = f[j]|a[i]; vt[f[j]] = true; } }
悲催的我连这个优化都没想到,不得不说思维啊。
不过这样肯定还是会TLE的。
后来看了一下别人的代码,才明白了。
首先我们取或操作实质是对一个数的二进制位添加1的过程。由于0<= a[i] <= 10^6 所以整体取或操作最大也就是10^6次大约20位。当枚举i是我们枚举i的每一位检查前边的数这一位是否出现,若出现过直接跳出,若没有出现过取或操作判断是否出现过,然后统计个数即可。
时间复杂度应该是O(n*20*20)最后是否乘20我也不是很确定,我个人认为得到最大数也就是将20位里面的每一位都置为1,这样我们如果每次添加一个1,最多添加20次即可得到结果。
个人理解如有错误请指正...
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <cstring> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 1000007 using namespace std; //freopen("din.txt","r",stdin); int a[M]; bool vt[1<<20]; int main(){ //freopen("din.txt","r",stdin); int i,j,k; int n; while (~scanf("%d",&n)){ CL(vt,false); int res = 0; for (i = 0; i < n; ++i){ scanf("%d",&a[i]); if (!vt[a[i]]){ vt[a[i]] = true; res++; } for (j = 0; j <= 20; ++j){//枚举每一位 if (a[i]&(1<<j)){ int st = a[i]; for (k = i - 1; k >= 0 && !(a[k]&(1<<j)); --k){//查看前边数的数这意味是否是1 st |= a[k]; if (!vt[st]){ vt[st] = true; res++; } } } } vt[a[i]] = true; } cout<<res<<endl; } return 0; }