「Luogu4755」Beautiful Pair-主席树+启发式分裂

Decription

小D有个数列 {a} { a } ,当一个数对 (ij)(ij) ( i ≤ j ) ( i ≤ j ) 满足 ai a i aj a j 的积不大于 ai,ai+1,,aj a i , a i + 1 , ⋯ , a j 中的最大值时,小D认为这个数对是美丽的.请你求出美丽的数对的数量。

Solution

考虑分治,对于区间 [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;
}

你可能感兴趣的:(文章类型——题解,数据结构——主席树,算法——分治/最值分治)