「LGR-049」洛谷7月月赛 D.Beautiful Pair

[「LGR-049」洛谷7月月赛 D.Beautiful Pair](https://www.luogu.org/problemnew/show/P4755)

题目大意 : 给出长度为 \(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;
}

posted @ 2018-07-15 19:20  Joyemang33  阅读(223)  评论(0编辑  收藏  举报