cdq分治思想(二维偏序)
将其中一维排序后,分离条件的相关性。将左半边的子问题用于解决右半边的子问题。
左半边和右半边靠递归完成,专注于处理跨越部分。
[l,r]工作完成后,可以根据需要选择将其按第二关键字(或任意顺序)排序,而不影响后续。
方法一:按x作为第一关键字排序
for j=mid+1 to r 枚举[l,j]的贡献和
由归并排序特点,[l,mid]和[mid+1,r]两个区间内已经按照第二关键字s排好序。寻找x<xj和x>xj的分界点,最后一个x<xj的点记为xi。
贡献和为
v
j
(
∑
k
=
l
i
(
x
j
−
x
k
)
+
∑
k
=
i
+
1
m
i
d
(
x
k
−
x
j
)
)
=
v
j
(
(
i
−
l
+
1
)
x
j
−
s
u
m
x
[
l
,
i
]
+
s
u
m
x
[
i
+
1
,
m
i
d
]
−
(
m
i
d
−
i
)
x
j
)
v_j(\sum_{k=l}^{i}(x_j-x_k)+\sum_{k=i+1}^{mid}(x_k-x_j) )=v_j((i-l+1)x_j-sumx[l,i]+sumx[i+1,mid]-(mid-i)x_j)
vj(k=l∑i(xj−xk)+k=i+1∑mid(xk−xj))=vj((i−l+1)xj−sumx[l,i]+sumx[i+1,mid]−(mid−i)xj)
前缀和预处理,查询复杂度O(1)。
贡献加完后,对第二关键字归并排序。
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
int x,v;
}a[50005];
bool cmp(node aa,node bb){
return aa.v<bb.v;//按v从小到大排序
}
int ans=0,sum[50005];
void merge(int l,int r){
int mid=(l+r)/2;
sum[l-1]=0;
for(int i=l;i<=r;i++){
sum[i]=sum[i-1]+a[i].x;
}
for(int j=mid+1;j<=r;j++){
int ii=l-1;//i:寻找x<xj的所有x,i标志分界线(最后一个满足的):由于归并排序,已经按x从小到大排序
while(ii<mid&&a[ii+1].x<a[j].x){
ii++;
}
ans+=a[j].v*((ii-l+1)*a[j].x-(sum[ii]-sum[l-1])+(sum[mid]-sum[ii])-(mid-ii)*a[j].x);
}
//中间跨越部分处理完成后,即可对合并的整体按第二关键字x排序,不影响后续结果。
node aux[r-l+1];
for(int i=l;i<=r;i++){
aux[i-l]=a[i];
}
int i1=l,i2=mid+1;
for(int i=l;i<=r;i++){
if(i1>mid){
a[i]=aux[i2-l];
i2++;
}else if(i2>r){
a[i]=aux[i1-l];
i1++;
}else if(aux[i1-l].x>=aux[i2-l].x){
a[i]=aux[i2-l];
i2++;
}else{
a[i]=aux[i1-l];
i1++;
}
}
}
void mergesort(int l,int r){
if(l>=r) return;
int mid=(l+r)/2;
mergesort(l,mid);
mergesort(mid+1,r);
merge(l,r);
}
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].v>>a[i].x;
}
sort(a,a+n,cmp);
mergesort(0,n-1);
cout<<ans;
}
方法二:按v作为第一关键字排序
与上同理,给出和式推导过程:
∑
k
=
l
i
v
j
(
x
j
−
x
k
)
+
∑
k
=
i
+
1
m
i
d
v
k
(
x
j
−
x
k
)
=
v
j
[
(
i
−
l
+
1
)
x
j
−
s
u
m
x
[
l
,
i
]
]
+
x
j
s
u
m
v
[
i
+
1
,
m
i
d
]
−
s
u
m
x
v
[
i
+
1
,
m
i
d
]
\sum_{k=l}^{i}v_j(x_j-x_k)+\sum_{k=i+1}^{mid}v_k(x_j-x_k)=v_j[(i-l+1)x_j-sumx[l,i]]+x_jsumv[i+1,mid]-sumxv[i+1,mid]
k=l∑ivj(xj−xk)+k=i+1∑midvk(xj−xk)=vj[(i−l+1)xj−sumx[l,i]]+xjsumv[i+1,mid]−sumxv[i+1,mid]
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
int x,v;
}a[50005];
bool cmp(node aa,node bb){
return aa.x<bb.x;//按x从小到大排序
}
int ans=0,sum_x[50005],sum_v[50005],sum_xv[50005];
void merge(int l,int r){
int mid=(l+r)/2;
sum_x[l-1]=0,sum_v[l-1]=0,sum_xv[l-1]=0;
for(int i=l;i<=r;i++){
sum_x[i]=sum_x[i-1]+a[i].x;
sum_v[i]=sum_v[i-1]+a[i].v;
sum_xv[i]=sum_xv[i-1]+a[i].v*a[i].x;
}
for(int j=mid+1;j<=r;j++){
int ii=l-1;//i:寻找v<vj的所有x,i标志分界线(最后一个满足的):由于归并排序,已经按x从小到大排序
while(ii<mid&&a[ii+1].v<a[j].v){
ii++;
}
ans+=a[j].v*((ii-l+1)*a[j].x-(sum_x[ii]-sum_x[l-1]))+a[j].x*(sum_v[mid]-sum_v[ii])-(sum_xv[mid]-sum_xv[ii]);
}
//中间跨越部分处理完成后,即可对合并的整体按第二关键字v排序,不影响后续结果。
node aux[r-l+1];
for(int i=l;i<=r;i++){
aux[i-l]=a[i];
}
int i1=l,i2=mid+1;
for(int i=l;i<=r;i++){
if(i1>mid){
a[i]=aux[i2-l];
i2++;
}else if(i2>r){
a[i]=aux[i1-l];
i1++;
}else if(aux[i1-l].v>=aux[i2-l].v){
a[i]=aux[i2-l];
i2++;
}else{
a[i]=aux[i1-l];
i1++;
}
}
}
void mergesort(int l,int r){
if(l>=r) return;
int mid=(l+r)/2;
mergesort(l,mid);
mergesort(mid+1,r);
merge(l,r);
}
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].v>>a[i].x;
}
sort(a,a+n,cmp);
mergesort(0,n-1);
cout<<ans;
}