「High Cry」Solution

简述题意

给定长度为 n n n 的数组 n n n,求出有多少个区间满足区间或大于区间最大值。

  • n ≤ 2 × 1 0 5 n \le 2 \times 10^5 n2×105

思路

从区间入手肯定不好做,考虑从最大值入手。

注意到一个区间,其肯定有一个最大值 a i a_i ai,因此统计区间个数就等价于统计对于 i ∈ [ 1 , n ] i \in [1,n] i[1,n],有多少个区间 [ l , r ] [l,r] [l,r] 满足 l ≤ i ≤ r l \le i \le r lir 且最大值等于 a i a_i ai 且区间或大于 a i a_i ai。注意到一个区间有多个最大值,因此钦定一个区间的最大值为最靠左的那一个。

区间最大值显然满足单调性,所以可以通过 ST \text{ST} ST 表加二分对每一个 i i i 预处理出 q l i , q r i ql_i,qr_i qli,qri,其含义为对于 ∀ l ∈ [ q l i , i ] , r ∈ [ i , q r i ] \forall l \in [ql_i,i],r\in[i,qr_i] l[qli,i],r[i,qri] [ l , r ] [l,r] [l,r] 的最大值为 a i a_i ai

接下来考虑区间或的限制。我们知道对于一个区间的或,很容易通过前缀数组 l o g ( n ) log(n) log(n) 求得,且在某个端点固定的情况下,随着另一个端点的延伸一定单调不降,因此也可以二分解决。对于每一个 i i i,不妨求出 l i , r i ( l i ≤ i , r i ≥ i ) l_i,r_i(l_i \le i,r_i\ge i) li,ri(lii,rii) ,其中 l i l_i li 表示离 i i i 最近且满足 [ l i , i ] [l_i,i] [li,i] 的区间或大于最大值的点,同理 r i r_i ri 表示离 i i i 最近且满足 [ i , r i ] [i,r_i] [i,ri] 的区间或大于最大值的点。那么对于一个区间 [ x , y ] [x,y] [x,y],如果其满足 x ∈ [ q l i , l i ] , y ∈ [ i , q r i ] x \in [ql_i,l_i],y \in [i,qr_i] x[qli,li]y[i,qri] 或者 x ∈ [ q l i , i ] , y ∈ [ r i , q r i ] x \in [ql_i,i],y\in[r_i,qr_i] x[qli,i],y[ri,qri],那么 [ x , y ] [x,y] [x,y] 就为题目所求的区间。

假设我们已求得 q l i , q r i , l i , r i ql_i,qr_i,l_i,r_i qli,qri,li,ri,那么某个 i ∈ [ 1 , n ] i \in [1,n] i[1,n] 对答案的贡献即为:
满足 x ∈ [ q l i , l i ] , y ∈ [ i , q r i ] x \in [ql_i,l_i],y \in [i,qr_i] x[qli,li]y[i,qri] 的区间个数 + + + 满足 x ∈ [ q l i , i ] , y ∈ [ r i , q r i ] x \in [ql_i,i],y\in[r_i,qr_i] x[qli,i],y[ri,qri] 的区间个数 - 满足 x ∈ [ q l i , l i ] , y ∈ [ r i , q r i ] x \in [ql_i,l_i],y \in [r_i,qr_i] x[qli,li]y[ri,qri] 的区间个数。推导基于容斥原理,且近乎显然。

代码

一道没太多细节的题,但是记得特殊处理 q l i ql_i qli q r i qr_i qri l i l_i li r i r_i ri 不存在的情况,且注意二分边界。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e5 + 5;
int n , pre[MAXN][31] , Max[MAXN][21] , a[MAXN] , ql[MAXN] , qr[MAXN] , ans;
vector<int> idx[MAXN];
int GetMax(int l , int r) {
	int s = log2(r - l + 1);
	return max(Max[l][s] , Max[r - (1 << s) + 1][s]);
}
bool check(int l , int r , int pos) {
	for (int v : idx[pos]) {
		if (pre[r][v] - pre[l - 1][v] > 0) return 1;
	}
	return 0;
}
int Findl(int pos) {
	int l = ql[pos] , r = pos;
	while(l < r) {
		int mid = l + r + 1 >> 1;
		if (check(mid , pos , pos)) l = mid;
		else r = mid - 1;
	}
	if (!check(l , pos , pos)) return -1;
	return l;
}
int Findr(int pos) {
	int l = pos , r = qr[pos];
	while(l < r) {
		int mid = l + r >> 1;
		if (check(pos , mid , pos)) r = mid;
		else l = mid + 1;
	}
	if (!check(pos , l , pos)) return -1;
	return l;
}
signed main() {
	ios::sync_with_stdio(false);
    cin.tie(nullptr) , cout.tie(nullptr);
	cin >> n;
	for (int i = 1 ; i <= n ; i ++) cin >> a[i] , Max[i][0] = a[i];
	for (int i = 1 ; i <= n ; i ++) {
		for (int j = 0 ; j <= 30 ; j ++) {
			if ((1 << j) & a[i]) pre[i][j] = pre[i - 1][j] + 1;
			else pre[i][j] = pre[i - 1][j] , idx[i].push_back(j);
		}
	}
	for (int j = 1 ; j <= 20 ; j ++) {
		for (int i = 1 ; i + (1 << j) - 1 <= n ; i ++) {
			Max[i][j] = max(Max[i][j - 1] , Max[i + (1 << j - 1)][j - 1]);
		}
	}
	a[0] = a[n + 1] = 0x3f3f3f3f;
	for (int i = 1 ; i <= n ; i ++) {
		if (a[i - 1] >= a[i] && a[i + 1] > a[i]) continue;
		int l = 1 , r = i - 1;
		while(l < r) {
			int mid = l + r >> 1;
			if (GetMax(mid , i - 1) < a[i]) r = mid;
			else l = mid + 1;
		}
		if (GetMax(l , i - 1) < a[i]) ql[i] = l;
		else ql[i] = i;
		l = i , r = n;
		while(l < r) {
			int mid = l + r + 1 >> 1;
			if (GetMax(i , mid) <= a[i]) l = mid;
			else r = mid - 1;
		}
		qr[i] = l;
	}
	for (int i = 1 ; i <= n ; i ++) {
		if (a[i - 1] >= a[i] && a[i + 1] > a[i]) continue;
		int l = Findl(i) , r = Findr(i);
		if (l == -1 && r == -1) continue;
		if (l == -1) ans += (i - ql[i] + 1) * (qr[i] - r + 1);
		else if (r == -1) ans += (l - ql[i] + 1) * (qr[i] - i + 1);
		else ans += (l - ql[i] + 1) * (qr[i] - i + 1) + (i - ql[i] + 1) * (qr[i] - r + 1) - (l - ql[i] + 1) * (qr[i] - r + 1);
	}
	cout << ans;
	return 0;
}
posted @ 2024-04-19 23:28  Fracture_Dream  阅读(9)  评论(0)    收藏  举报  来源