Codeforces Round #441 (Div. 2)F. High Cry 按位考虑 + 思维

题意 :就是给你 n 个数让你求满足 这个区间内最大的数小于这个区间内所有数或起来的区间个数。
题解 : 正面处理这个问题比较麻烦我们就先求出不满足的区间,然后用总的区间减去,不满足的就是答案。
剩下的问题就是怎么求不满足的区间呢 ?
首先我们要求出每个数的控制范围 (就是说掌控的意思是这个数or所有其他数等于自己)
首先按位考虑这个问题 如果当前的数的某一位是1那么这一位就可以不考虑,如果这一位是零那么考虑这一位是1的最靠近 当前考虑的数的位置。并且记录下来,正着做一遍,反着做一遍,就可以得到这个的控制的范围,然后答案就是 ans - (i - l[i]) * (r[i] - i) 就可以了
注意两点
1. 更新的时候32位的每一位都要跟着数的更新而更新
2. 在第一遍更新的时候要用一个 map 去解决可能出现数字重复的问题。

细节看代码 很短 很好懂

#include 
#include 
#include 
#include 
#include 
#include 
#define ll long long
using namespace std;

const int maxn = 2e5 + 10;
ll n;
int pos[33] = {0};
ll ans = 0;
int a[maxn] = {0};
map <int,int> mp;
int l[maxn] = {0};
int r[maxn] = {0};
int main () {
    ios_base :: sync_with_stdio(false);
    cin >> n;
    ans = (n + 1) * n / 2;
    for (int i = 1; i <= n; ++ i) {
        cin >> a[i];
    }
    for (int i = 1;i <= n; ++ i) {
        l[i] = mp[a[i]];
        for (int j = 0;j < 32; ++ j) {
            if (!(a[i] & (1 << j))) {
                l[i] = max (l[i],pos[j]);
            }
        }
        for (int j = 0;j < 32; ++ j) {
            if (a[i] & (1 << j)) {
                pos[j] = i;
            }
        }
        mp[a[i]] = i;
    }
    for (int i = 0;i < 32; ++ i) pos[i] = n + 1;
    for (int i = n; i; -- i) {
        r[i] = n + 1;
        for (int j = 0;j < 32; ++ j) {
            if (!(a[i] & (1 << j))) {
                r[i] = min (pos[j],r[i]);
            }
        }
        for (int j = 0;j < 32; ++ j) {
            if (a[i] & (1 << j)) {
                pos[j] = i;
            }
        }
        ans -= (ll)(i - l[i]) * (ll)(r[i] - i);
    }
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(思维题,codeforces)