P4755 Beautiful Pair (数据结构+分治)
Posted 昵称很长很长真是太好了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4755 Beautiful Pair (数据结构+分治)相关的知识,希望对你有一定的参考价值。
题意:
小D有个数列a ,当一个数对
(
i
,
j
)
(
i
≤
j
)
(i,j)(i\\leq j)
(i,j)(i≤j)满足
a
i
a_i
ai和
a
j
a_j
aj 的积不大于
a
i
,
a
i
+
1
,
.
.
.
.
.
.
.
.
.
,
a
j
a_i, a_{i+1},.........,a_j
ai,ai+1,.........,aj中的最大值时,小D认为这个数对是美丽的.请你求出美丽的数对的数量。
n
≤
1
e
5
n \\leq1e5
n≤1e5
a
i
≤
1
e
9
a_i \\leq1e9
ai≤1e9
有人推荐笛卡尔树的练习题?但是怎么也想不到用笛卡尔树做…
跟在笛卡尔树上分治一样…只不过ST表换成了笛卡尔树
题解:
分治+主席树+ST表
每次找出区间
[
l
,
r
]
[l,r]
[l,r]之间最大值的下标
m
i
d
mid
mid(ST表查找即可)
然后枚举
l
到
m
i
d
l到mid
l到mid的每个数,每次区间询问mid到r之间数小于等于
⌊
a
m
i
d
a
i
⌋
\\lfloor \\frac{a_{mid}}{a_i} \\rfloor
⌊aiamid⌋
但是我们会发现,极端情况下会被卡成
O
(
n
2
l
o
g
2
n
)
O(n^2log^2n)
O(n2log2n)
那么我们借用启发式的思想,每次去枚举区间长度小的那一半,查询大的一半即可。
为什么这样做是正确的呢?
这其实就相当于枚举了每一个点,进行查询,并且不会出现重复的情况。
比如说当前查找的区间为
[
l
,
r
]
[l,r]
[l,r],查询出来的数对数量是跨过mid的数对数量,下一个查询的区间就是
[
l
,
m
i
d
−
1
]
,
[
m
i
d
+
1
,
r
]
[l,mid-1],[mid+1,r]
[l,mid−1],[mid+1,r]这两个小区间了,因为这两个小区间根本不会存在跨过mid的数对,那个大区间也不存在不跨过的数对,所以不存在重复的情况。
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
using namespace std;
const int maxn=1e5+10;
int a[maxn],ans;
int sum[maxn*50];
int lc[maxn*50],rc[maxn*50];
int rt[maxn],cnt,n,p;
void update(int &node,int start,int ends,int lst,int pos){
node=++cnt;
lc[node]=lc[lst];
rc[node]=rc[lst];
sum[node]=sum[lst]+1;
if(start==ends) return ;
int mid=(start+ends)/2;
if(pos<=mid) update(lc[node],start,mid,lc[lst],pos);
else update(rc[node],mid+1,ends,rc[lst],pos);
}
int query(int start,int ends,int l,int r,int x,int y){
if(l<=start&&ends<=r){
return sum[y]-sum[x];
}
int mid=(start+ends)>>1;
int ans=0;
if(l<=mid) ans+=query(start,mid,l,r,lc[x],lc[y]);
if(mid<r) ans+=query(mid+1,ends,l,r,rc[x],rc[y]);
return ans;
}
int stmax[maxn][21];
int mn[maxn];
int stcmpmax(int x,int y){
return a[x]>a[y]?x:y;
}
void init(){
mn[0]=-1;
for (int i=1;i<=n;i++){
mn[i]=((i & (i-1))==0) ? mn[i-1]+1 : mn[i-1];
stmax[i][0]=i;
}
for (int j=1;j<=mn[n];j++)
for (int i=1;i+(1<<j)-1<=n;i++)
{
stmax[i][j]=stcmpmax(stmax[i][j-1],stmax[i+(1<<(j-1))][j-1]);
}
}
int rmq_max(int L,int R)
{
int k=mn[R-L+1];
return stcmpmax(stmax[L][k],stmax[R-(1<<k)+1][k]);
}
void divide(int l,int r){
if(r<l) return ;
if(l==r){
if(a[l]==1) ans++;
//cout<<"debug "<<a[l]<<endl;
return ;
}
int pre=ans;
int mid=rmq_max(l,r);
//cout<<"debug "<<mid<<endl;
if(mid-l<r-mid){
for(int i=l;i<=mid;i++){
int nowx=a[mid]/a[i];
ans+=query(1,p,1,nowx,rt[mid-1],rt[r]);
}
}
else{
for(int i=mid;i<=r;i++){
int nowx=a[mid]/a[i];
ans+=query(1,p,1,nowx,rt[l-1],rt[mid]);
}
}
//cout<<"debug "<<l<<" "<<r<<" "<<mid<<" "<<ans-pre<<endl;
divide(l,mid-1);
divide(mid+1,r);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
//freopen("out.txt","w",stdout);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
p=max(p,a[i]);
}
init();
for(int i=1;i<=n;i++){
update(rt[i],1,p,rt[i-1],a[i]);
}
divide(1,n);
cout<<ans<<endl;
}
以上是关于P4755 Beautiful Pair (数据结构+分治)的主要内容,如果未能解决你的问题,请参考以下文章
luoguP4755 Beautiful Pair 笛卡尔树+线段树合并+启发式合并