牛客网 牛客练习赛16 E.求值

链接:https://www.nowcoder.com/acm/contest/84/E
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给定n个数字a1, a2, ..., an。
定义f(l, r) = al | al+1| ... | ar。
现在枚举(1 <= l <= r <= n),问不同的f值一共有多少个。
输入描述:

第一行一个整数n表示数组大小 (1 <= n <= 100,000);
第二行n个整数满足0 <= ai <= 1000,000。

输出描述:

输出一个整数表示不同的f值一共有多少个。

示例1
输入

3
1 2 0

输出

4

示例2
输入

10
1 2 3 4 5 6 1 2 9 10

输出

11

 

分析:可以发现,根据题目的范围,每个数最多有20个二进制位,所以我们可以先枚举每个区间开头的数,比如现在枚举到了i,那么它产生新值的下一个位置的数,应在i为0的二进制位上,新数的该位为1,且为了不遗漏,新数必须最近。。而为了找到这个最近的位置,可以进行预处理,Next[i][j]用来表示从第i个数开始,第j个二进制位为1的最近的位置,这样的话时间复杂度就是O(20*20*n),能够满足。

代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN=1e5+100;
int vis[1000010*2];
int Next[MAXN][22];
int a[MAXN];
int tmp;
int main()
{
    int n;
    int ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    scanf("%d",&a[i]);
     if(a[i]==0&&!vis[0])
     {
     vis[0]=1;
     ans++;
     }
    }

    for(int i=0;i<=19;i++)
    {
      if(a[n]&(1<<i))
      Next[n][i]=n;
      else
      Next[n][i]=n+1;
    }
    for(int i=n-1;i>=1;i--)
    {
        for(int j=0;j<=19;j++)
        {
           if(a[i]&(1<<j))
           Next[i][j]=i;
           else
           Next[i][j]=Next[i+1][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
       int p=i;
       int now=0;
       while(1)
       {

          int minpos=MAXN;

          for(int j=0;j<=19;j++)
          {
              if(!(now&(1<<j)))
              minpos=min(minpos,Next[p][j]);
          }
          if(minpos>n)break;
          now|=a[minpos];
          if(!vis[now])
          {
              vis[now]=1;
              ans++;
          }
          p=minpos;
       }
    }
    printf("%d\n",ans);

    return 0;
}

 

posted @ 2018-04-30 21:38  hinata_hajime  阅读(212)  评论(0编辑  收藏  举报