洛谷 P4755 - Beautiful Pair(主席树+分治+启发式优化)
wssb,我紫菜
看到这类与最大值统计有关的问题可以很自然地想到分治,考虑对 \([l,r]\) 进行分治,求出对于所有 \(l\le x\le y\le r\) 的点对 \((x,y)\) 的贡献之和。若 \(l=r\) 那只有 \(a_l=1\) 的情况会产生贡献,特判一下并直接返回即可。若 \(l\ne r\),我们假设 \([l,r]\) 中最大值的位置为 \(mid\),考虑将所有符合要求的点对分成三部分,一是 \(l\le x\le y<mid\),二是 \(mid<x\le y\le r\),三是 \(l\le x\le mid\le y\le r\),对于前两种情况,分别对区间 \([l,mid-1],[mid+1,r]\) 进行分治即可,对于第三种情况,显然 \([x,y]\) 中最大值就是 \(a_{mid}\),考虑枚举 \(x\in[l,mid]\),那么显然只有 \(a_y\le\dfrac{a_{mid}}{a_x},y\in[mid,r]\) 的 \(y\) 才可能产生贡献,这相当于一个静态区间求值域在某个区间范围内数的个数,可用离散化+主席树解决。这样做可能会被极端数据卡到 \(n^2\log n\),不过考虑加一个优化,那就是如果区间 \([l,mid]\) 长度 \(<[mid+1,r]\) 的长度,就枚举 \(x\in[l,mid]\),否则枚举 \(y\in[mid,r]\),这样时间复杂度为 \(T(n)=\max\limits_{t=0}^nT(t)+T(n-t)+\min(t,n-t)\log n\),打个表可知 \(T(n)=n\log^2n\),可通过此题。
感觉这题从哪方面说都不难啊,为啥我想不到呢?大概是我做题太少了没遇到过类似的套路罢……
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int LOG_N=17;
int n,a[MAXN+5],key[MAXN+5],uni[MAXN+5],num=0;
struct node{int ch[2],val;} s[MAXN*40+5];
int rt[MAXN+5],ncnt=0;
void build(int &k,int l,int r){
k=++ncnt;if(l==r) return;int mid=l+r>>1;
build(s[k].ch[0],l,mid);build(s[k].ch[1],mid+1,r);
}
int modify(int k,int l,int r,int p,int x){
int id=++ncnt;s[id]=s[k];s[id].val+=x;
if(l==r) return id;int mid=l+r>>1;
if(p<=mid) s[id].ch[0]=modify(s[k].ch[0],l,mid,p,x);
else s[id].ch[1]=modify(s[k].ch[1],mid+1,r,p,x);
return id;
}
int query(int k,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return s[k].val;int mid=l+r>>1;
if(qr<=mid) return query(s[k].ch[0],l,mid,ql,qr);
else if(ql>mid) return query(s[k].ch[1],mid+1,r,ql,qr);
else return query(s[k].ch[0],l,mid,ql,mid)+query(s[k].ch[1],mid+1,r,mid+1,qr);
}
pii st[MAXN+5][LOG_N+2];
pii getpos(int l,int r){
int k=31-__builtin_clz(r-l+1);
return max(st[l][k],st[r-(1<<k)+1][k]);
}
ll ans=0;
void solve(int l,int r){
if(l>r) return;if(l==r){ans+=(uni[a[l]]==1);return;}
int mid=getpos(l,r).se;solve(l,mid-1);solve(mid+1,r);
if(mid-l<=r-mid){
for(int i=l;i<=mid;i++){
int val=uni[a[mid]]/uni[a[i]];
val=upper_bound(uni+1,uni+num+1,val)-uni-1;
if(val) ans+=query(rt[r],1,num,1,val)-query(rt[mid-1],1,num,1,val);
}
} else {
for(int i=mid;i<=r;i++){
int val=uni[a[mid]]/uni[a[i]];
val=upper_bound(uni+1,uni+num+1,val)-uni-1;
if(val) ans+=query(rt[mid],1,num,1,val)-query(rt[l-1],1,num,1,val);
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),key[i]=a[i];
sort(key+1,key+n+1);
for(int i=1;i<=n;i++) if(key[i]^key[i-1]) uni[++num]=key[i];
for(int i=1;i<=n;i++) a[i]=lower_bound(uni+1,uni+num+1,a[i])-uni;
build(rt[0],1,num);
for(int i=1;i<=n;i++) rt[i]=modify(rt[i-1],1,num,a[i],1);
for(int i=1;i<=n;i++) st[i][0]=mp(a[i],i);
for(int i=1;i<=LOG_N;i++) for(int j=1;j+(1<<i)-1<=n;j++)
st[j][i]=max(st[j][i-1],st[j+(1<<i-1)][i-1]);
solve(1,n);printf("%lld\n",ans);
return 0;
}