题目传送门

题目大意

给出一个 \(n\) 个点的序列 \(a_{1,2,...,n}\) ,问有多少对点对 \((i,j)\) 满足 \(a_i\times a_j\le a_k(i\le k\le j)\)。

\(n\le 10^5,1\le a_i\le 10^9\)

思路

话说为什么裸的笛卡尔树上分治可以骗到 \(90\) 分啊???

首先不难看出一个比较 naive 的做法,就是说我们可以考虑最大堆得笛卡尔树上的一个子树,如果左端点在左子树,右端点在右子树,那么最大值就是根,然后其实就是统计 \(\lfloor\frac{k}{a_i}\rfloor\) ,其中 \(k\) 表示根的值。具体实现就是直接摊平然后当序列搞就好了,不过你可以发现其实不需要摊平。

然后我们发现这样做时间复杂度在单调的序列中时间复杂度就会降到 \(\Theta(n^2\log n)\),我们考虑启发式合并,即是说我们每次选较小的子树进行查询,然后区间小于等于某个数的个数可以使用主席树进行维护。

考虑这样做的时间复杂度,我们考虑对于每个点的查询次数,可以想到,它作为最小值得时候爬树的时候每次子树大小都至少扩大一倍,于是最多就被访问到 \(\log n\) 次,所以总时间复杂度就是 \(\Theta(n\log^2 n)\) 。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define ll long long
#define MAXN 100005 char buf[1 << 21],*p1 = buf,*p2 = buf;
#define getchar() ((p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin))) ? EOF : *p1 ++)
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} ll ans;
int n,top,len,ls[MAXN],rs[MAXN],val[MAXN],tur[MAXN],sta[MAXN],root[MAXN]; void buildTree (){
for (Int i = 1;i <= n;++ i){
int tmp = top;
while (top && val[sta[top]] < val[i]) -- top;
if (top) rs[sta[top]] = i;
if (top < tmp) ls[i] = sta[top + 1];
sta[++ top] = i;
}
} struct Segment{
#define LOG 21
int cnt,sum[MAXN * LOG],ls[MAXN * LOG],rs[MAXN * LOG];
void modify (int &x,int y,int l,int r,int pos){
x = ++ cnt,sum[x] = sum[y] + 1,ls[x] = ls[y],rs[x] = rs[y];
if (l == r) return ;
int mid = (l + r) >> 1;
if (pos <= mid) modify (ls[x],ls[y],l,mid,pos);
else modify (rs[x],rs[y],mid + 1,r,pos);
}
int query (int x,int l,int r,int tl,int tr){
if (!x || (l >= tl && r <= tr)) return sum[x];
int mid = (l + r) >> 1,res = 0;
if (tl <= mid) res += query (ls[x],l,mid,tl,tr);
if (tr > mid) res += query (rs[x],mid + 1,r,tl,tr);
return res;
}
int query (int l,int r,int tl,int tr){
return query (root[r],1,len,tl,tr) - query (root[l - 1],1,len,tl,tr);
}
}Tree; void divide (int x,int l,int r){
if (!ls[x] && !rs[x]) return ans += (tur[val[x]] == 1),void ();
if (x - l < r - x){
for (Int i = l;i <= x;++ i){
int ind = upper_bound (tur + 1,tur + len + 1,tur[val[x]] / tur[val[i]]) - tur - 1;
if (!ind) continue;
ans += Tree.query (x,r,1,ind);
}
}
else{
for (Int i = x;i <= r;++ i){
int ind = upper_bound (tur + 1,tur + len + 1,tur[val[x]] / tur[val[i]]) - tur - 1;
if (!ind) continue;
ans += Tree.query (l,x,1,ind);
}
}
if (ls[x]) divide (ls[x],l,x - 1);
if (rs[x]) divide (rs[x],x + 1,r);
} signed main(){
read (n);
for (Int i = 1;i <= n;++ i) read (val[i]),tur[i] = val[i];
sort (tur + 1,tur + n + 1),len = unique (tur + 1,tur + n + 1) - tur - 1;
for (Int i = 1;i <= n;++ i) val[i] = lower_bound (tur + 1,tur + len + 1,val[i]) - tur;
buildTree ();for (Int i = 1;i <= n;++ i) Tree.modify (root[i],root[i - 1],1,len,val[i]);
divide (sta[1],1,n),write (ans),putchar ('\n');
return 0;
}

题解 Beautiful Pair的更多相关文章

  1. 【题解】P4755 Beautiful Pair(启发式合并的思路+分治=启发式分治)

    [题解]P4755 Beautiful Pair upd: 之前一个first second烦了,现在AC了 由于之前是直接抄std写的,所以没有什么心得体会,今天自己写写发现 不知道为啥\(90\) ...

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

    「LGR-049」洛谷7月月赛 D.Beautiful Pair 题目大意 : 给出长度为 \(n\) 的序列,求满足 \(i \leq j\) 且 $a_i \times a_j \leq \max ...

  3. [luogu4755]Beautiful Pair

    [luogu4755]Beautiful Pair luogu 第一次写最大值分治感觉有点丑 每次找到最大值mid,扫小的一边,主席树查大的一边小于等于\(\frac{a[mid]}{a[i]}\)的 ...

  4. Luogu4755 Beautiful Pair 最值分治、主席树

    传送门 整天做一些模板题感觉药丸 设\(val_i\)表示第\(i\)个位置的值 看到区间最大值考虑最值分治.对于当前的区间\([l,r]\),找到区间最大值\(mid\),递归\([l,mid-1] ...

  5. P4755 Beautiful Pair (分治 + 主席树)

    题意:1e5的数组 计算有多少对 ai * aj <= max(ai ai+1...aj-1 aj) 题解:在处理这种涉及到区间极值的题时 好像是个套路分治 从级值中间分成两个区间 从区间短的那 ...

  6. 洛谷 P4755 - Beautiful Pair(主席树+分治+启发式优化)

    题面传送门 wssb,我紫菜 看到这类与最大值统计有关的问题可以很自然地想到分治,考虑对 \([l,r]\) 进行分治,求出对于所有 \(l\le x\le y\le r\) 的点对 \((x,y)\ ...

  7. luoguP4755 Beautiful Pair

    https://www.luogu.org/problemnew/show/P4755 考虑分治,在 [l, r] 区间中用线段树找到最大的一个点,处理经过它的可行数对的个数,统计个数可以离线树状数组 ...

  8. Luogu 4755 Beautiful Pair

    分治 + 主席树. 设$solve(l, r)$表示当前处理到$[l, r]$区间的情况,我们可以找到$[l, r]$中最大的一个数的位置$mid$,然后扫一半区间计算一下这个区间的答案. 注意,这时 ...

  9. P4755 Beautiful Pair

    题目 洛谷 做法 \(i≤x≤j,a[i]<\frac{a[x]}{a[j]}\) 考虑\(a[x]\)的贡献,单调栈预处理\(L,R\)能作为最大值的区间 枚举一端点,仅需另一端点满足条件即可 ...

随机推荐

  1. python实现两台不同主机之间进行通信(客户端和服务端)——Socket

    大家好,我是辰哥~ 今天教大家通过Python进行Socket网络编程 (做一个聊天程序) 可以实现在不同的主机(电脑)之间进行通话. 具体效果如何,接着往下看 可以看到客户端(上方)向服务器端(下方 ...

  2. redis百万级数据存取

    Jedis jedis0 = new Jedis("localhost", 6379); jedis0.auth("123456"); Pipeline pip ...

  3. Mysql 日期格式化 复杂日期区间查询

    前言 最近在做项目涉及到Mysql的复杂日期查询,日期查询其实在数据库中查询其实还是用的挺多的,比如查询开始日期到结束日期的区间信息,查询日期小于有效日期的信息,查询当天的日期,明天的日期,做比较等. ...

  4. Linux 单实例oracle安装步骤

    一.查看逻辑盘大小,执行 lsblk 二.查看硬盘及分区信息 ,执行 fdisk -l 三.将物理硬盘分区初始化为物理卷,以便LVM使用 ,创建pv pvcreate /dev/sdb 四.查看物理卷 ...

  5. OAuth2-简介

    1. 简介 OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用.因此OAUTH是安全的. ...

  6. APT组织跟踪与溯源

    前言 在攻防演练中,高质量的蓝队报告往往需要溯源到攻击团队.国内黑产犯罪团伙.国外APT攻击. 红队现阶段对自己的信息保护的往往较好,根据以往溯源成功案例来看还是通过前端js获取用户ID信息.mysq ...

  7. java的split方法中的regex参数

    我们需要以|进行分割,为了匹配|本身,正则中采用\|进行转义,而Java中\也表示转义,从java到正则需要必须使用\\|进行转义,即split中的\\表示正则的转义.

  8. leetcode 盛水最多的容器 解析

    采用双指针法: 主要思想:定义头尾两个指针,分别向中间遍历,当相遇时结束循环.存储每一次遍历容器盛水的最大容量,并不断更新. 盛水的最大容量为 左右指针高度的最小值 乘以 左右指针的距离即宽度. 则可 ...

  9. PHP中操作任意精度大小的GMP扩展学习

    对于各类开发语言来说,整数都有一个最大的位数,如果超过位数就无法显示或者操作了.其实,这也是一种精度越界之后产生的精度丢失问题.在我们的 PHP 代码中,最大的整数非常大,我们可以通过 PHP_INT ...

  10. 学习PHP中的任意精度扩展函数

    今天来学习的是关于数学方面的第一个扩展.对于数学操作来说,无非就是那些各种各样的数学运算,当然,整个程序软件的开发过程中,数学运算也是最基础最根本的东西之一.不管你是学得什么专业,到最后基本上都会要学 ...