AtCoder acl1_d - Keep Distances

这是个挺平凡的 DS 题感觉。不知道为啥有 2800?

考虑对于一个区间,先算出最大的 \(|s|\)。那么贪心地,显然可以从 \(l\) 开始往后,每次找到最左边一个 keep 住 distance 的地方跳过去直到到 \(r\) 右边为止。

然后发现,\(s\) 中最右边一个位置一直到 \(r\) 都是可以当作 \(s\) 的最后一个的,也即最后一位的取值范围是一个区间。然后考虑倒数第二个位置的取值范围,左端点显然是原来的,右端点基于贪心是上一个区间的右端点往左跳。以此类推可以算出每个位置的取值范围。

稍微想想,每个区间显然不可能有 \(k\) 个,这说明区间们不相交;然后显然的,右端点就是把之前找左端点的那个过程反过来,从 \(r\) 往左跳,它们得出的个数显然都是最大的 \(|s|\),相等,于是左端点和右端点数量匹配。这两点证明了上面那个的合理性。

然后答案就是所有区间的个数和。那就是 \(\sum(r_i-l_i+1)\),拆开来是 \(\sum r_i+\sum(-l_i+1)\)。由于左端点和右端点的计算是独立的,于是可以分别计算。这个倍增随便搞搞就完事了。

#include<bits/stdc++.h>
using namespace std;
const int N=200000,LOG_N=20;
int n,m,qu;
int a[N+1];
int rit[N+1],lft[N+1];
int rit_to[N+1][LOG_N],rit_sum[N+1][LOG_N],lft_to[N+1][LOG_N],lft_sum[N+1][LOG_N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	int now=1;
	for(int i=1;i<=n;i++){
		while(now<=n&&a[now]-a[i]<m)now++;
		rit[i]=now;
	}
	rit[n+1]=n+1;
	for(int i=n+1;i;i--){
		rit_to[i][0]=rit[i],rit_sum[i][0]=-rit[i]+1;
		for(int j=1;j<LOG_N;j++)
			rit_to[i][j]=rit_to[rit_to[i][j-1]][j-1],
			rit_sum[i][j]=rit_sum[i][j-1]+rit_sum[rit_to[i][j-1]][j-1];
	}
	now=n;
	for(int i=n;i;i--){
		while(now&&a[i]-a[now]<m)now--;
		lft[i]=now;
	}
	lft[0]=0;
	for(int i=0;i<=n;i++){
		lft_to[i][0]=lft[i],lft_sum[i][0]=lft[i];
		for(int j=1;j<LOG_N;j++)
			lft_to[i][j]=lft_to[lft_to[i][j-1]][j-1],
			lft_sum[i][j]=lft_sum[i][j-1]+lft_sum[lft_to[i][j-1]][j-1];
	}
	cin>>qu;
	while(qu--){
		int l,r;
		scanf("%d%d",&l,&r);
		int ans=0;
		now=l;
		ans+=-now+1;
		for(int i=LOG_N-1;~i;i--)if(rit_to[now][i]<=r)ans+=rit_sum[now][i],now=rit_to[now][i];
		now=r;
		ans+=now;
		for(int i=LOG_N-1;~i;i--)if(lft_to[now][i]>=l)ans+=lft_sum[now][i],now=lft_to[now][i];
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2020-09-21 22:04  ycx060617  阅读(168)  评论(1编辑  收藏  举报