CF1156E Special Segments of Permutation 和 LG4755 Beautiful Pair
CF1156E Special Segments of Permutation
给定一个长度为\(n\)的排列\(p\),求有多少区间\([l,r]\)满足,\(p_l+p_r=\max_{l\leq i\leq r}\{p_i\}\)
\(n\leq 2\times 10^5\)
题解
先预处理出每个 \(i\) 左边第一个大于它的位置(记为 \(L[i]\) )和右边第一个大于它的位置(记为 \(R[i]\) )。显然可以单调栈 \(O(n)\) 求出。
枚举最大值的位置,那么左端点只会落在 \((L[i],i)\) ,右端点只会落在 \((i,R[i])\) 。
那么在小的那个区间中枚举一个端点,在另外那个区间中查有没有对应的值。
这样子看上去是 \(O(n^2)\) 的,实际上是 \(O(n\log n)\) 的。
它的本质是在笛卡尔树上启发式分裂。(虽然并没有建出笛卡尔树来)
CO int N=2e5+10;
int a[N],ia[N];
int stk[N],L[N],R[N];
int main(){
int n=read<int>();
for(int i=1;i<=n;++i) ia[read(a[i])]=i;
int top=0;
for(int i=1;i<=n;++i){
while(top and a[stk[top]]<a[i]) --top;
L[i]=stk[top],stk[++top]=i;
}
stk[top=0]=n+1;
for(int i=n;i>=1;--i){
while(top and a[stk[top]]<a[i]) --top;
R[i]=stk[top],stk[++top]=i;
}
int ans=0;
for(int i=1;i<=n;++i){
if(i-L[i]<R[i]-i){
for(int j=L[i]+1;j<i;++j){
int p=ia[a[i]-a[j]];
ans+=i<p and p<R[i];
}
}
else{
for(int j=i+1;j<R[i];++j){
int p=ia[a[i]-a[j]];
ans+=L[i]<p and p<i;
}
}
}
printf("%d\n",ans);
return 0;
}
LG4755 Beautiful Pair
小D有个数列\(\{a\}\),当一个数对\((i,j)(i\le j)\)满足\(a_i\)和\(a_j\)的积不大于\(a_i,a_{i+1},\cdots,a_j\)中的最大值时,小D认为这个数对是美丽的.请你求出美丽的数对的数量。
\(1\le n\le{10}^5,1\le a_i\le{10}^9\)
题解
跟上一道题差不多,只不过细节多了很多。上一道题隐含条件\(l<r\),这题就没有了。
具体细节见代码,我好像就是凭直觉瞎改了一下就过了。
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#define pbds __gnu_pbds
using namespace std;
#define CO const
#define IN inline
typedef long long int64;
typedef pbds::tree<std::pair<int,int>,pbds::null_type,std::less<std::pair<int,int> >,pbds::rb_tree_tag,pbds::tree_order_statistics_node_update> tree;
template<class T> IN T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T> IN T read(T&x){
return x=read<T>();
}
CO int N=1e5+10;
int n,a[N];
int stk[N],L[N],R[N];
vector<int> que[N];
tree sum;
int main(){
read(n);
for(int i=1;i<=n;++i) read(a[i]);
int top=0;
for(int i=1;i<=n;++i){
while(top and a[stk[top]]<a[i]) --top; // left <
L[i]=stk[top],stk[++top]=i;
}
stk[top=0]=n+1;
for(int i=n;i>=1;--i){
while(top and a[stk[top]]<=a[i]) --top; // right <=
R[i]=stk[top],stk[++top]=i;
}
for(int i=1;i<=n;++i){
if(i-L[i]<R[i]-i){
for(int j=L[i]+1;j<=i;++j){
que[R[i]-1].push_back(a[i]/a[j]);
que[i-1].push_back(-(a[i]/a[j]));
}
}
else{
for(int j=i;j<R[i];++j){
que[i].push_back(a[i]/a[j]);
que[L[i]].push_back(-(a[i]/a[j]));
}
}
}
int64 ans=0;
for(int i=1;i<=n;++i){
sum.insert({a[i],i});
for(int x:que[i]){
if(x>0) ans+=sum.order_of_key({x+1,0});
else ans-=sum.order_of_key({-x+1,0});
}
}
printf("%lld\n",ans);
return 0;
}
静渊以有谋,疏通而知事。