洛谷 P5677 [GZOI2017]配对统计(树状数组)
传送门
解题思路
注意有个条件为ai互不相同。
尝试应用lxl教的套路:
第一步,将一维问题放到二维平面上:把好的配对(x,y)看做二维平面上的点。于是问题就变成了求出矩形所包含的点的个数。
第二步,利用数据结构将二维平面问题降到一维解决:离线,把询问按照右端点排序,同时把好的配对按照右端点排序,保证在树状数组里的好的配对的x和y都小于等于当前的r。这样每次查询等于在树状数组里查询(l,r)的和。
注意事项与细节:
- 特判n==0的情况
- 对于点(x,y)要保证x<=y
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=3e5+5;
int n,m,cnt,now;
long long ans,d[maxn];
struct node{
int l,r,id;
long long v;
}a[maxn],b[maxn],q[maxn];
bool cmp(node a,node b){
return a.r<b.r;
}
bool cmp2(node a,node b){
return a.v<b.v;
}
inline int lowbit(int x){
return x&(-x);
}
void update(int x,int v){
for(int i=x;i<=n;i+=lowbit(i)){
d[i]+=v;
}
}
long long query(int x){
long long res=0;
for(int i=x;i>=1;i-=lowbit(i)){
res+=d[i];
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
if(n==1){
cout<<0;
return 0;
}
for(int i=1;i<=n;i++) cin>>a[i].v,a[i].id=i;
sort(a+1,a+n+1,cmp2);
a[0].v=-1e9;
a[n+1].v=2e9;
for(int i=1;i<=n;i++){
if(a[i].v-a[i-1].v<a[i+1].v-a[i].v){
b[++cnt].l=min(a[i-1].id,a[i].id);
b[cnt].r=max(a[i-1].id,a[i].id);
}else{
if(a[i].v-a[i-1].v==a[i+1].v-a[i].v){
b[++cnt].l=min(a[i-1].id,a[i].id);
b[cnt].r=max(a[i-1].id,a[i].id);
b[++cnt].l=min(a[i].id,a[i+1].id);
b[cnt].r=max(a[i].id,a[i+1].id);
}else{
b[++cnt].l=min(a[i].id,a[i+1].id);
b[cnt].r=max(a[i].id,a[i+1].id);
}
}
}
sort(b+1,b+cnt+1,cmp);
for(int i=1;i<=m;i++){
cin>>q[i].l>>q[i].r;
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
for(int i=1;i<=m;i++){
while(now<cnt&&b[now+1].r<=q[i].r){
now++;
update(b[now].l,1);
}
ans+=1ll*q[i].id*(query(q[i].r)-query(q[i].l-1));
while(i<m&&q[i+1].r==q[i].r){
i++;
ans+=1ll*q[i].id*(query(q[i].r)-query(q[i].l-1));
}
}
cout<<ans;
return 0;
}