题目大意
给出一个序列 a,设 f(l,r) 表示 al∣al+1∣al+2∣⋯∣ar,求 f(l,r) 有多少不同的值。
思路
显然,对于任意 l,f(l,r)≤f(l,r+1),也就是说,对于从同一个数开始的所有区间,它们的值一定是随长度增加而增加的,所以,我们假设有一个变量 t,它从 al 开始不停地与下一个数相或,这个 t 的值增加的次数就是所有以 al 为左端点的区间中不同的 f(l,r) 的值的数量,因为 t 只会增大,不会变小。
那么我们怎么知道这个 t 变大了多少次呢?考虑 t 在什么情况下会变大,显然,t 会变大,当且仅当原来 t 为 0 的位变成了 1,而且每一位最多会变一次,所以,我们维护一个 nxt 数组,用 nxti,j 表示 ai 到 an 这些数中第 j 位为 1 的第一个数的下标,由于有些位可能同时变成 1,所以我们还需要对下标进行去重。
最终时间复杂度 O(nlogM),常数稍大。
参考代码
对于 nxt 数组,这里采用的是滚动数组的方法,减小了一点空间复杂度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+10; struct QwQ { int val; int id; bool operator < (const QwQ &a) const { return val == a.val ? id < a.id : val < a.val; } }; QwQ t[30]; int n, mx, a[N], b[N*15], nxt[30]; ll ans; int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = n; i >= 1; i--) { int x = a[i]; for(int j = 0; j <= 20; j++) { t[j] = (QwQ){nxt[j], j}; if((a[i]>>j)&1) nxt[j] = i; } sort(t, t+21); for(int j = 0; j <= 20; j++) if(t[j].val) { if(!b[x] && (j == 0 || t[j].val != t[j-1].val)) { ans++; b[x] = 1; } x |= (1<<t[j].id); } if(!b[x]) { ans++; b[x] = 1; } } printf("%lld", ans); return 0; }
|