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;
}

  

 

 

 

转载于:https://www.cnblogs.com/E-star/archive/2012/11/19/2777904.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值