牛客网 牛客练习赛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; }