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;
}

浙公网安备 33010602011771号