P5677 [GZOI2017]配对统计做题笔记

一道花了两天的题目,主要是因为死活找不出 bug。从树状数组题单里翻出来的,看了第一篇题解。主要思路是把输入的点从小到大排序,统计“好的配对”的数量,统计方法为若当前数和两边的数的差的绝对值相等则当前数和两边的数都构成“好的配对”,否则当前数和两边数中与当前数差较小的那个数构成“好的配对”。注意:若 \(a_i\)\(a_j\)能构成“好的配对”,\(a_j\)\(a_i\)不一定能构成“好的配对”,如 1 3 4\(1\)\(3\) 能构成“好的配对”,但 \(3\)\(1\) 不能构成“好的配对”,因为 \(3\)\(4\) 构成“好的配对”(\(4-3 < 3-1\))。 然后将“好的配对”按右端点从小到大排序,再把读入的询问按右端点从小到大排序,进行离线处理。循环遍历排序后的询问,没遍历到一个询问,就把该询问右端点以左的“好的配对”加入树状数组中,当前询问结果为当前树状数组中“好的配对”的数量减去树状数组中右端点在该询问左端点以右的“好的配对”数量。

#include <bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,l,tt;
long long s[300005],ans;
struct xy{
  long long x,y,z;
}a[300005],b[300005],t[600005];
bool cmp1(xy u,xy v)
{
  return u.x<v.x;
}
bool cmp2(xy u,xy v)
{
  if (u.y==v.y) return u.x<v.x;
  return u.y<v.y;
}
void Addpair(int u,int v)
{
  t[++l].x=min(a[u].y,a[v].y);
  t[l].y=max(a[u].y,a[v].y);
}
void Add(int t,long long u)
{
  for (int j=t;j<=n;j+=j&(-j))
  {
  	s[j]+=1ll*u;
  }
}
long long getsum(int t)
{
  long long ans=0;
  for (int j=t;j;j-=j&(-j))
  {
  	ans+=s[j];
  }
  return ans;
}
signed main()
{
  ios::sync_with_stdio(0); cout.tie(nullptr);
  cin>>n>>m;
  if (n==1)
  {
  	cout<<0<<endl;
  	return 0;
  }
  for (int i=1;i<=n;i++)
  {
  	cin>>a[i].x;
  	a[i].y=i;
  }
  sort(a+1,a+n+1,cmp1);
  Addpair(1,2);
  Addpair(n-1,n);
  for (int i=2;i<n;i++)
  {
  	if (a[i].x-a[i-1].x==a[i+1].x-a[i].x)
  	{
  		Addpair(i-1,i);
  		Addpair(i,i+1);
  	}
  	else if (a[i].x-a[i-1].x<a[i+1].x-a[i].x) Addpair(i,i-1);
  	else Addpair(i,i+1);
  }
  sort(t+1,t+l+1,cmp2);
  for (int i=1;i<=m;i++)
  {
  	cin>>b[i].x>>b[i].y;
  	b[i].z=i;
  }
  sort(b+1,b+m+1,cmp2);
  tt=1;
  for (int i=1;i<=m;i++)
  {
  	while (t[tt].y<=b[i].y && tt<=l) Add(t[tt++].x,1);
  	ans+=1ll*b[i].z*(tt-1-getsum(b[i].x-1));
  }
  cout<<ans<<endl;
  return 0;
}
posted @ 2022-08-20 15:06  Jason142  阅读(65)  评论(0编辑  收藏  举报