【LGR-049】洛谷7月月赛 D.Beautiful Pair


>【LGR-049】洛谷7月月赛 D.Beautiful Pair <

题目大意 : 给出长度为 \(n\) 的序列,求满足 \(i \leq j\) 且 $a_i \times a_j \leq \max(a_i..a_j) $ 的点对\((i, j)\)的数量
\(n \leq 10^5 \ 1 \leq a_i \leq 10^9\)

解题思路 :

直接枚举某一端点貌似很难维护极值,不妨对于每一个极值 \(a_k\),求 $ \max(a_i..a_j) \leq a_k$ 且 \(i \leq k \leq j\) 的点对数
考虑一个每一个点能作为\(\max\)统治的区间可以用单调栈求,并构建出笛卡尔树

此时对于每一个 \(a_k\) 答案由两部分产生:

  1. 点对 \((i, j)\) 满足 \(i = k\) 或 $j = k $,这一部分答案比较简单,就是统治区间内 \(1\) 的个数
  2. 点对 \((i, j)\) 满足 \(i \neq k\)\(j \neq k\) ,此时答案等价于点 \(k\) 在笛卡尔树上的节点的左右子树中各取一个组成合法点对的方案数

第一种情况可以直接前缀和计算,在此不多做讨论,主要看第二种情况

由于是树上的子树问题,我们很容易想到用 \(dsu \ on\ tree\) 的做法来解决

不了解 \(dsu \ on \ tree\) 的可以戳这里的链接

枚举子树大小较小的子树,用树状数组维护较大的子树产生的贡献

对于较小子树中的点 \(u\) ,能与其组成合法点对的点 \(x\) 要满足 \(\frac{maxval}{a_u} \leq a_x\),直接用树状数组统计前缀和即可



/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define N (500005)
#define int ll
int a[N], b[N], n, ans;
namespace Ls{
    int s[N]; int lens = 0;
    inline int calc(int x){
        int res = upper_bound(s + 1, s + lens + 1, x) - s - 1;
        return res;
    }
    inline void Realmain(){
        for(int i = 1; i <= n; i++) s[i] = a[i];
        sort(s + 1, s + n + 1);
        lens = unique(s + 1, s + n + 1) - s - 1;
    }
}
struct Fenwick{
    int s[N];
    inline int lowbit(int x){ return x & -x; }
    inline void add(int x, int v){
        for(int i = x; i <= n; i += lowbit(i)) s[i] += v;
    }
    inline int query(int x){
        int ans = 0;
        for(int i = x; i; i -= lowbit(i)) ans += s[i];
        return ans;
    }
}Bit;
namespace Treap{
    int sz[N], ch[N][2], s[N], l[N], r[N], st[N], top;
    inline void Getsize(int u){
        sz[u] = 1; 
        int ls = ch[u][0], rs = ch[u][1];
        if(ls) Getsize(ls), sz[u] += sz[ls];
        if(rs) Getsize(rs), sz[u] += sz[rs];
        if(!u) sz[u] = 0;
    }
    inline void Add(int u){
        Bit.add(b[u], 1);
        if(ch[u][0]) Add(ch[u][0]);
        if(ch[u][1]) Add(ch[u][1]);
    }
    inline void Clear(int u){
        Bit.add(b[u], -1);
        if(ch[u][0]) Clear(ch[u][0]);
        if(ch[u][1]) Clear(ch[u][1]);
    }
    inline int Count(int u, int val){
        int pos = Ls::calc(val / a[u]);
        int res = Bit.query(pos);
        if(ch[u][0]) res += Count(ch[u][0], val);
        if(ch[u][1]) res += Count(ch[u][1], val);
        return res;
    }
    inline void solve(int u){
        int p = sz[ch[u][0]] > sz[ch[u][1]] ? 0 : 1;
        int ms = ch[u][p], ls = ch[u][p^1];
        if(ls) solve(ls), Clear(ls);
        if(ms) solve(ms); 
        if(ls) ans += Count(ls, a[u]);
        if(u){ if(ls) Add(ls); Bit.add(b[u], 1); } 
    }
    inline void Build(){
        for(int i = 1; i <= n; i++){
            while(a[st[top]] <= a[i] && top) r[st[top--]] = i - 1;
            int f = st[top]; l[i] = f + 1;
            ch[i][0] = ch[f][1], ch[f][1] = i, st[++top] = i; 
        }
        for(int i = 1; i <= n; i++) if(!r[i]) r[i] = n;
    }
    inline void Realmain(){
        Build(), Getsize(0), solve(0);
        for(int i = 1; i <= n; i++) s[i] = s[i-1] + (a[i] == 1 ? 1 : 0);
        for(int i = 1; i <= n; i++) ans += s[r[i]] - s[l[i]-1];
        cout << ans << endl;
    }
}
main(){
    read(n);
    for(int i = 1; i <= n; i++) read(a[i]);
    Ls::Realmain();
    for(int i = 1; i <= n; i++) b[i] = Ls::calc(a[i]);
    Treap::Realmain();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/mangoyang/p/9314552.html
今日推荐