P5677 [GZOI2017]配对统计

提供两种与众不同的做法。

Link\text{Link}

题意

若在整个数列中距离 axa_x 最近的数包含 aya_y,则称 (x,y)(x,y) 是好的配对(为了方便叙述,本文中认为 axa_x 配对了 aya_yaya_yaxa_x 配对了)。多组询问,每次询问区间 [l,r][l,r] 中有多少组好的配对。

分析

首先可以注意到,若 (x,y)(x,y) 是好的配对,则 (y,x)(y,x) 不一定是,手动模拟即可明白。

其次题目中保证 iji\neq j 时,aiaja_i\neq a_j,所以猜测正解与值域有关,但是 1ai1091\le a_i\le 10^9 这个范围令人十分不爽,于是我们考虑把它离散化掉:1ain1\le a_i\le n 且当 iji\neq j 时,aiaja_i\neq a_j

这个时候可以发现一个有意思的性质:对于 axa_x,能被他配对的 aya_y 只能ax1a_x-1ax+1a_x+1 或二者均可。

又因为没有修改操作,于是我们可以预处理:

  • ee 数组,eai=ie_{a_i}=i
  • flfl 数组,flaxfl_{a_x} 表示小于 axa_x能被 axa_x 配对的数 aya_y,没有则为 00
  • fflffl 数组,fflaxffl_{a_x} 表示小于 axa_x能配对 axa_x 的数 aya_y,没有则为 00
  • frfr 数组,fraxfr_{a_x} 表示大于 axa_x能被 axa_x 配对的数 aya_y,没有则为 00
  • ffrffr 数组,ffraxffr_{a_x} 表示大于 axa_x能配对 axa_x 的数 aya_y,没有则为 00
for(int i=1;i<=n;i++){//b数组是离散化前的a数组
	if(a[i]==n||(a[i]>1&&b[a[i]]-b[a[i]-1]<=b[a[i]+1]-b[a[i]])){
		fl[a[i]]=a[i]-1;
		ffr[a[i]-1]=a[i];
	}
	if(a[i]==1||(a[i]<n&&b[a[i]]-b[a[i]-1]>=b[a[i]+1]-b[a[i]])){
		fr[a[i]]=a[i]+1;
		ffl[a[i]+1]=a[i];
	}
}

然后开一个数组 fxf_x 表示 xx 是否在当前状态下存在,接下来就可以很快地进行插入和删除一个数了。

  • 若要插入一个数 axa_x,只需要判断可能与其配对的数 flax,fflax,frax,ffraxfl_{a_x},ffl_{a_x},fr_{a_x},ffr_{a_x} 是否存在即可,把存在的数量加入到答案里。
  • 若要删除一个数 axa_x,只需要判断可能与其配对的数 flax,fflax,frax,ffraxfl_{a_x},ffl_{a_x},fr_{a_x},ffr_{a_x} 是否存在即可,把存在的数量从答案里减去。

这为接下来两种做法的实现奠定了基础。

莫队

既然我们掌握了快速插入和删除一个数的方法,那么只需要把询问离线,再套莫队板子即可。

然而最后一个点 TLE 了,要加上玄学的奇偶性排序优化才可以过。

树状数组

莫队这么压线的做法肯定不是正解。

于是考虑从题目本身出发。

首先,看到求区间某种东西的个数时,我总是立刻有一个平凡而大胆的想法:

“既然要求 [l,r][l,r],那么先求出前缀和 [1,r][1,r][1,l1][1,l-1] 再相减不就行了么!”

想法很美好,然而现实很残酷。

因为这样做漏减了左右值分别在 [1,l1][1,l-1][l,r][l,r] 的好的配对,于是开始思考:怎样求出它呢?

其实,我们可以把一个好的配对 (x,y)(x,y) 认为是一个区间 [x,y][x,y](假设是 x<yx<y 的情况),那么上面要求的那个值就相当于求所有包含 l1l-1 的区间 [x,y1][x,y-1] 的个数(假定当前只加入了 a1ara_1\sim a_r),也就是使 l1l-1 不是区间右端点的包含 l1l-1 的区间的个数,这样保证了这些区间都横跨 [1,l1][1,l-1][l,r][l,r]

这样我们只要写某种数据结构存下当前状态左右值分别在 [1,l1][1,l-1][l,r][l,r] 的好的配对,每次我们加入一个配对 (x,y)(x,y) 时,只要把 [x,y1][x,y-1] 的每个数增加 11 即可,不难想到运用包含差分思想的树状数组来解决这个问题。

代码

莫队

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N=4e5+10;
int n,m,len;
ll a[N],b[N],cnt,ans;
int fl[N],fr[N],f[N],ffl[N],ffr[N];
struct asdf{
	int l,r,id;
}d[N];
bool cmp(asdf x,asdf y){
	return (x.l/len<y.l/len||(x.l/len==y.l/len&&((x.l/len)&1?x.r<y.r:x.r>y.r)));
}
void add(int x){
	f[x]=1;
	cnt+=f[fl[x]]+f[fr[x]]+f[ffl[x]]+f[ffr[x]];
}
void del(int x){
	f[x]=0;
	cnt-=f[fl[x]]+f[fr[x]]+f[ffl[x]]+f[ffr[x]];
}
int main(){
	n=read();m=read();
	len=sqrt(m);
	for(int i=1;i<=n;i++)
		b[i]=a[i]=read();
	sort(b+1,b+n+1);
	for(int i=1;i<=n;i++)
		a[i]=lower_bound(b+1,b+n+1,a[i])-b;
	for(int i=1;i<=n;i++){
		if(a[i]==n||(a[i]>1&&b[a[i]]-b[a[i]-1]<=b[a[i]+1]-b[a[i]])){
			fl[a[i]]=a[i]-1;
			ffr[a[i]-1]=a[i];
		}
		if(a[i]==1||(a[i]<n&&b[a[i]]-b[a[i]-1]>=b[a[i]+1]-b[a[i]])){
			fr[a[i]]=a[i]+1;
			ffl[a[i]+1]=a[i];
		}
	}
	for(int i=1;i<=m;i++){
		d[i].l=read();
		d[i].r=read();
		d[i].id=i;
	}
	sort(d+1,d+m+1,cmp);
	int ql=1,qr=0;
	for(int i=1;i<=m;i++){
		while(ql>d[i].l) add(a[--ql]);
		while(qr<d[i].r) add(a[++qr]);
		while(ql<d[i].l) del(a[ql++]);
		while(qr>d[i].r) del(a[qr--]);
		ans+=cnt*d[i].id;
	}
	cout<<ans<<endl;
	return 0;
}

树状数组

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N=4e5+10;
int n,m;
ll a[N],b[N],cnt,ans,sum[N],c[N],e[N];
int fl[N],fr[N],f[N],ffl[N],ffr[N];
struct asdf{
	int l,r,id;
}d[N];
bool cmp(asdf x,asdf y){
	return x.r<y.r;
}
void Add(int x,int v){
	for(;x<=n;x+=x&-x)
		c[x]+=v;
}
ll ask(int x){
	ll ans=0;
	for(;x;x-=x&-x)
		ans+=c[x];
	return ans;
}
void add(int x){
	f[a[x]]=1;
	if(f[fl[a[x]]]){
		sum[x]++;
		Add(e[fl[a[x]]],1);
		Add(x,-1);
	}
	if(f[fr[a[x]]]){
		sum[x]++;
		Add(e[fr[a[x]]],1);
		Add(x,-1);
	}
	if(f[ffl[a[x]]]){
		sum[x]++;
		Add(e[ffl[a[x]]],1);
		Add(x,-1);
	}
	if(f[ffr[a[x]]]){
		sum[x]++;
		Add(e[ffr[a[x]]],1);
		Add(x,-1);
	}
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)
		b[i]=a[i]=read();
	sort(b+1,b+n+1);
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(b+1,b+n+1,a[i])-b;
		e[a[i]]=i;
	}
	for(int i=1;i<=n;i++){
		if(a[i]==n||(a[i]>1&&b[a[i]]-b[a[i]-1]<=b[a[i]+1]-b[a[i]])){
			fl[a[i]]=a[i]-1;
			ffr[a[i]-1]=a[i];
		}
		if(a[i]==1||(a[i]<n&&b[a[i]]-b[a[i]-1]>=b[a[i]+1]-b[a[i]])){
			fr[a[i]]=a[i]+1;
			ffl[a[i]+1]=a[i];
		}
	}
	for(int i=1;i<=m;i++){
		d[i].l=read();
		d[i].r=read();
		d[i].id=i;
	}
	sort(d+1,d+m+1,cmp);
	int q=0;
	for(int i=1;i<=m;i++){
		while(q<d[i].r){
			sum[q+1]=sum[q];
			q++;
			add(q);
		}
		ans+=(sum[d[i].r]-sum[d[i].l-1]-ask(d[i].l-1))*d[i].id;
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2022-03-14 21:23  luckydrawbox  阅读(7)  评论(0)    收藏  举报  来源