小D有个数列 {a} { a } ,当一个数对 (i≤j)(i≤j) ( i ≤ j ) ( i ≤ j ) 满足 ai a i 和 aj a j 的积不大于 ai,ai+1,⋯,aj a i , a i + 1 , ⋯ , a j 中的最大值时,小D认为这个数对是美丽的.请你求出美丽的数对的数量。
考虑分治,对于区间 [L,R] [ L , R ] 。每次找到区间中的最小值所在位置 minpos m i n p o s ,然后枚举 [l,minpos] [ l , m i n p o s ] 与 (minpos,r] ( m i n p o s , r ] 中长度较小的一段,然后查询在另一区间中能与它配对的个数,累计即可。
单次处理 O(minsizelog2maxsize) O ( m i n s i z e l o g 2 m a x s i z e ) ,总复杂度 O(nlog2) O ( n l o g 2 )
#include
using namespace std;
typedef long long lint;
const int maxn = 100005;
int n, a[maxn], maxa, Log[maxn], Max[20][maxn], pos[20][maxn];
lint ans;
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
int getmin(int l, int r)
{
int k = Log[r - l + 1];
if (Max[k][l] > Max[k][r - (1 << k) + 1]) return pos[k][l];
else return pos[k][r - (1 << k) + 1];
}
#define mid ((l + r) >> 1)
int root[maxn], tot, ch[maxn * 40][2], sum[maxn * 40];
void insert(int s1, int &s2, int l, int r, int v)
{
s2 = ++tot;
ch[s2][0] = ch[s1][0]; ch[s2][1] = ch[s1][1]; sum[s2] = sum[s1];
++sum[s2];
if (l == r) return;
if (v <= mid) insert(ch[s1][0], ch[s2][0], l, mid, v);
else insert(ch[s1][1], ch[s2][1], mid + 1, r, v);
}
int query(int s1, int s2, int l, int r, int v)
{
if (l == r) return sum[s2] - sum[s1];
if (v <= mid) return query(ch[s1][0], ch[s2][0], l, mid, v);
else return sum[ch[s2][0]] - sum[ch[s1][0]] + query(ch[s1][1], ch[s2][1], mid + 1, r, v);
}
void solve(int l, int r)
{
if (l == r) {
if (a[l] == 1) ++ans;
return;
}
int maxpos = getmin(l, r), L, R, x, y;
if (maxpos - l > r - maxpos) x = maxpos + 1, y = r, L = l, R = maxpos - 1;
else x = l, y = maxpos - 1, L = maxpos + 1, R = r;
for (int i = x; i <= y; ++i)
ans += query(root[L - 1], root[R], 1, maxa, a[maxpos] / a[i]);
ans += query(root[l - 1], root[r], 1, maxa, 1);
if (l < maxpos) solve(l, maxpos - 1);
if (maxpos < r) solve(maxpos + 1, r);
}
int main()
{
n = gi();
for (int i = 1; i <= n; ++i) a[i] = gi(), maxa = max(maxa, a[i]);
Log[1] = 0;
for (int i = 2; i <= n; ++i) Log[i] = Log[i >> 1] + 1;
for (int i = 1; i <= n; ++i) Max[0][i] = a[i], pos[0][i] = i;
for (int j = 0; (1 << j) < n; ++j)
for (int i = 1; i + (1 << (j + 1)) - 1 <= n; ++i) {
if (Max[j][i] > Max[j][i + (1 << j)]) Max[j + 1][i] = Max[j][i], pos[j + 1][i] = pos[j][i];
else Max[j + 1][i] = Max[j][i + (1 << j)], pos[j + 1][i] = pos[j][i + (1 << j)];
}
for (int i = 1; i <= n; ++i)
insert(root[i - 1], root[i], 1, maxa, a[i]);
solve(1, n);
printf("%lld\n", ans);
return 0;
}