P7405-[JOI 2021 Final]雪玉【二分】

正题

题目链接:https://www.luogu.com.cn/problem/P7405


题目大意

(n)个点在坐标轴上,(q)次每次所有点向一个方向移动若干步,每个点的权值是它第一次覆盖的区间长度(也就是一个区间只能贡献到第一次经过它的点)。

求所有点的最终权值。

(1leq n,qleq 2 imes 10^5)


解题思路

因为两个点的区间只会被这两个点覆盖,所以考虑求出每个区间被两边各占了多少。

先去掉无用的条件,求出一个数组(f)满足正负交替表示一左一右,正负的绝对值各自递增。

然后在(f)数组上二分出两个点覆盖的区间第一次相交的时候就可以计算各自被覆盖多少了。

时间复杂度(O(nlog n))


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2e5+10;
ll n,m,Q,lmax,rmax,x[N],g[N],f[N],w[N];
signed main()
{
	scanf("%lld%lld",&n,&Q);
	for(ll i=1;i<=n;i++)scanf("%lld",&x[i]);
	for(ll i=1;i<=Q;i++){
		scanf("%lld",&g[i]);g[i]+=g[i-1];
		if(g[i]>rmax){
			if(f[m]<=0)rmax=f[++m]=g[i];
			else rmax=f[m]=g[i];
		}
		if(g[i]<lmax){
			if(f[m]>=0)lmax=f[++m]=g[i];
			else lmax=f[m]=g[i];
		}
	}
	w[1]-=lmax;w[n]+=rmax;
	for(ll i=1;i<n;i++){
		ll len=x[i+1]-x[i];
		ll l=0,r=m-2;
		while(l<=r){
			ll mid=(l+r)>>1;
			if(abs(f[mid])+abs(f[mid+1])>=len)r=mid-1;
			else l=mid+1;
		}
		if(abs(f[l])+abs(f[l+1])>=len){
			if(f[l+1]>0)w[i+1]-=f[l],w[i]+=len+f[l];
			else w[i]+=f[l],w[i+1]+=len-f[l];
		}
		else{
			if(f[l+1]>0)w[i+1]-=f[l],w[i]+=f[l+1];
			else w[i]+=f[l],w[i+1]-=f[l+1];
		}
	}
	for(ll i=1;i<=n;i++)
		printf("%lld
",w[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/15026574.html