codeforces 875D - High Cry
http://codeforces.com/contest/875/problem/D
题意:给你 n 个长度的数组,求一共有多少区间 [ l , r ] ,使区间 [ l , r ] 所有数字的 或 运算大于所有区间 [ l , r ] 的数。
题解:RMQ+分治。
找到 区间 [ l , r ] 内最大值 a,和最接近 a 的数 b 且(b|a>a),左边的数为 lb,右边的数为rb,经过 a 的区间可以 o(1)求出。分治 a 的两边。
#include<cstdio> #include<string> #include<cmath> #include<algorithm> #include<iostream> #include<cstring> #include<set> #define oo 0x3f3f3f3f #define mod 1000000007 using namespace std; const int MAXN = 200000+10; int dp[MAXN][20]; int a[MAXN]; vector<int> ve[35]; void RMQ_max_init( int n ) { memset(dp, 0x00, sizeof(dp)); for(int i = 1; i <= n; i++) dp[i][0] = i; for(int j = 1; (1<<j) <= n; j++) for(int i = 1; i+(1<<j)-1 <= n; i++) if( a[ dp[i][j-1] ] < a[ dp[i+(1<<(j-1))][j-1] ]) dp[i][j] = dp[i+(1<<(j-1))][j-1]; else dp[i][j] = dp[i][j-1]; } int RMQ_max( int L, int R ) { int k = 0; while( (1<<(k+1)) <= R-L+1 ) k++; if(a[ dp[L][k] ] < a[ dp[R-(1<<k)+1][k] ]) return dp[R-(1<<k)+1][k]; return dp[L][k]; } void init(int x, int id) { for(int i = 0; (1<<i) <= x; ++i) { if(x&(1<<i)) ve[i].push_back(id); } } long long ans; void dfs(int l, int r, int n) { if(l>=r) return ; int mid = RMQ_max(l, r); int left = 0, right = n+1; for(int i = 0; (1<<i)<=1e9; ++i) { if(!(a[mid]&(1<<i))) { int k = lower_bound(ve[i].begin(), ve[i].end(), mid)-ve[i].begin(); if(k < ve[i].size() && ve[i][k]>mid) right = min(right, ve[i][k] ); --k; if(k>=0 && k < ve[i].size() && ve[i][k] < mid) left = max(left, ve[i][k]); } } if(left != 0 && left >= l) { ans += (long long)(left-l+1)* (long long)(r-mid+1); } if(right != n+1 && right <= r) { ans += (long long)(mid-l+1)* (long long)(r-right+1); } if(left != 0 && left >= l && right != n+1 && right <= r) { ans -= (long long)(left-l+1)* (long long)(r-right+1); } dfs(l, mid-1, n); dfs(mid+1, r, n); } int main (void) { ios::sync_with_stdio(false); int n; cin >> n; for(int i = 1; i <= n; ++i) { cin >> a[i]; init( a[i], i ); } RMQ_max_init(n); ans = 0; dfs(1, n, n); cout << ans; }