洛谷 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;
}
posted @ 2021-09-15 20:06  尹昱钦  阅读(42)  评论(0编辑  收藏  举报