P5677 [GZOI2017]配对统计

首先 具体有哪些点对我们是很好找到的

问题转化为 多次询问区间 有多少点对

对于多次区间离线询问 考虑定一边R 从左到右依次选择

对询问的R从小到大排序

这题树状数组还是很好想的 考虑一个点对【l,r】 一个询问【L,R】

如果r>R 那么这个区间一定不会考虑这个点对 所以有点对r<=R时才加入树状数组

对于每个点对【l,r】l加入树状数组 每次询问 只询问l>=L的点就好

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=3e5+5;
int n,m,cnt;
int c[maxn];
void upd(int x){
	while(x<maxn)c[x]++,x+=lowbit(x);
}
int query(int x){
	int res=0;
	while(x)res+=c[x],x-=lowbit(x);
	return res;
}
struct node{
	int val,id;
}a[maxn];
struct ee{
	int L,R,ii;
}p[maxn],ask[maxn];
bool cmp(node aa,node bb){
	return aa.val<bb.val;
}
bool cmp1(ee aa,ee bb){
	return aa.R<bb.R;
}
void add(int k1,int k2){
	p[++cnt].L=min(k1,k2);
	p[cnt].R=max(k1,k2);
}
int main(){
	cin>>n>>m;
	if(n==1){
		cout<<"0"<<endl;
		return 0; 
	}
	for(int i=1;i<=n;i++)scanf("%d",&a[i].val),a[i].id=i;
	sort(a+1,a+1+n,cmp);
	add(a[1].id,a[2].id);
	add(a[n-1].id,a[n].id);
	for(int i=2;i<=n-1;i++){
		int c1=a[i].val-a[i-1].val;
		int c2=a[i+1].val-a[i].val;
		if(c1>c2)add(a[i+1].id,a[i].id);
		else if(c1<c2)add(a[i-1].id,a[i].id);
		else add(a[i+1].id,a[i].id),add(a[i].id,a[i-1].id);
	}
	for(int i=1;i<=m;i++)
	scanf("%d%d",&ask[i].L,&ask[i].R),ask[i].ii=i;
	sort(ask+1,ask+1+m,cmp1);
	sort(p+1,p+1+cnt,cmp1);
	ll j=1,res=0;
	for(int i=1;i<=m;i++){
		while(j<=cnt&&p[j].R<=ask[i].R){
			upd(p[j].L);
			j++;
		}
		res+=1ll*ask[i].ii*(j-1-query(ask[i].L-1));
	}
	cout<<res<<endl;
     return 0;
}
posted @ 2022-04-11 15:59  wzx_believer  阅读(71)  评论(0)    收藏  举报