luoguP4755 Beautiful Pair (笛卡尔树+线段树合并+启发式合并)
新学了一下笛卡尔树,这道题是模板题,统计一下以 $x$ 为 lca 的点对就行.
然后统计的话用线段树合并 + 启发式合并就行了.
code:
#include <bits/stdc++.h> #define N 100006 #define ll long long #define lson s[x].ls #define rson s[x].rs #define MAX 1000000000 #define setIO(s) freopen(s".in","r",stdin) using namespace std; ll an,ans[N]; int n,tot; int val[N],L[N],R[N],sta[N],fa[N],RT[N],size[N]; struct seg { int ls,rs,sum; }s[N*70]; void dfs(int x) { size[x]=1; if(L[x]) fa[L[x]]=x,dfs(L[x]),size[x]+=size[L[x]]; if(R[x]) fa[R[x]]=x,dfs(R[x]),size[x]+=size[R[x]]; } void update(int &x,int l,int r,int p,int v) { if(!x) x=++tot; s[x].sum+=v; if(l==r) return; int mid=(l+r)>>1; if(p<=mid) update(lson,l,mid,p,v); else update(rson,mid+1,r,p,v); } int merge(int x,int y) { if(!x||!y) return x+y; int now=++tot; s[now].sum=s[x].sum+s[y].sum; s[now].ls=merge(s[x].ls,s[y].ls); s[now].rs=merge(s[x].rs,s[y].rs); return now; } int query(int x,int l,int r,int L,int R) { if(!x) return 0; if(l>=L&&r<=R) return s[x].sum; int mid=(l+r)>>1,re=0; if(L<=mid) re+=query(lson,l,mid,L,R); if(R>mid) re+=query(rson,mid+1,r,L,R); return re; } int calc(int tmp,int ma,int y) { int R=ma/tmp; return R<1?0:query(RT[y],1,MAX,1,R); } void dfs2(int x,int ma,int y) { an+=(ll)calc(val[x],ma,y); if(L[x]) dfs2(L[x],ma,y); if(R[x]) dfs2(R[x],ma,y); } void solve(int x) { an+=(ll)(val[x]==1); if(L[x]) { solve(L[x]); an+=(ll)calc(val[x],val[x],L[x]); } if(R[x]) { solve(R[x]); an+=(ll)calc(val[x],val[x],R[x]); } if(L[x]&&R[x]) { if(size[L[x]]<size[R[x]]) dfs2(L[x],val[x],R[x]); else dfs2(R[x],val[x],L[x]); } update(RT[x],1,MAX,val[x],1); if(L[x]) RT[x]=merge(RT[x],RT[L[x]]); if(R[x]) RT[x]=merge(RT[x],RT[R[x]]); } int main() { // setIO("input"); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&val[i]); int top=0,rt=0; for(int i=1;i<=n;++i) { while(top&&val[i]>=val[sta[top]]) L[i]=sta[top],--top; if(top) R[sta[top]]=i; sta[++top]=i; } dfs(rt=sta[1]); solve(rt); printf("%lld\n",an); return 0; }