Description
给定长度为2N的序列,1~N各处现过2次,i第一次出现位置记为ai,第二次记为bi,求满足ai<aj<bi<bj的对数
题解:
方法一:
搞一个KDtree,数一下点即可.
方法二:
对于每个数存一下左端点 $l[a]$ 与右端点 $r[a]$, 按照左端点从小到大进行排序.
枚举到每个数字,就将左端点对应的桶 ++, 右端点对应的桶 --,每次对答案的更新为 $sum[l]$ - $sum[r]$.
为什么呢 ?
假设左端点的 $sum$ 为 $a$, 右端点的 $sum$ 为 $b$.
那么, 说明在左端点这个位置上有 $a$ 个数字区间已经启动,却没有终止(感性理解一下).
而 $a-b$ 则代表在 $[l[a],r[a]]$ 区间中终止的个数. 这不就是会对该区间有贡献的个数吗 ?
前缀和用树状数组维护即可.
code:
#include<bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 200001
using namespace std;
int arr[maxn],l[maxn],r[maxn],A[maxn],C[maxn];
int cmp(int a,int b)
{
return l[a]<l[b];
}
int lowbit(int t)
{
return t&(-t);
}
void update(int x,int delta)
{
while(x<maxn) C[x]+=delta,x+=lowbit(x);
}
int query(int x)
{
int tmp=0;
while(x>0) tmp+=C[x],x-=lowbit(x);
return tmp;
}
int main(){
// setIO("input");
int n,m;
scanf("%d",&n),m=(n<<1) ;
for(int i=1,a;i<=m;++i)
{
scanf("%d",&a);
if(l[a])
r[a]=i;
else
l[a]=i;
}
for(int i=1;i<=n;++i) A[i]=i;
sort(A+1,A+1+n,cmp);
int ans=0;
for(int i=1;i<=n;++i)
{
int cur=A[i],a;
a=query(l[cur])-query(r[cur]);
ans+=a;
update(l[cur],1),update(r[cur],-1);
}
printf("%d\n",ans);
return 0;
}