2019.03.04【ZJOI2018】【洛谷P4501】【BZOJ5308】胖(ST表)(二分答案)

BZOJ传送门

洛谷传送门


解析:

ZJOI2018对我现阶段而言唯一一道可做的题。(剩下全是神仙题做之前先%jiry_2)

我们发现这个图长得很有特点,而且Bellman-Ford第一步肯定是更新直接连接的那些节点。然后再在链上扩展。

现在考虑一个点与 0 0 0有边的点 u u u,它能够更新的显然是包含它的一个连续区间,我们需要考虑求出这个区间的左右端点 l , r l,r l,r,那么就能够得到它更新的区间长度就是 r − l + 1 r-l+1 rl+1

u u u能够更新 v v v的条件也很显然,设 l = ∣ u − v ∣ l=|u-v| l=uv,则 u u u应该是 [ v − l , v + l ] [v-l,v+l] [vl,v+l]中的特殊点中到 v v v最近的。当然我们还要考虑相等距离取坐标差小的问题。

显然我们可以根据 u u u v v v的哪边进行分类讨论。设 v a l u val_u valu 0 0 0 u u u的距离, d i s t u dist_u distu是链上 u u u 1 1 1的距离,用于差分。

d i s ( u , v ) = { v a l u − u + v u ≤ v v a l u + u − v v &lt; u dis(u,v)=\left\{\begin{aligned}val_u-u+v &amp;&amp;u \leq v\\val_u+u-v &amp;&amp;v&lt; u\end{aligned}\right. dis(u,v)={valuu+vvalu+uvuvv<u

维护两个ST表就行了。

不开O2的话就不要用lower_bound和upper_bound了,手写吧。

不过开了O2就没什么区别了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=2e5+5,logN=18;

int n,m,K;

struct edge{
	int to,dis;
	bool operator<(cs edge &b)cs{
		return to<b.to;
	}
}e[N],tmp;

namespace ST{
	ll l[logN][N],r[logN][N];
	int logn[N];
	inline void init(){
		for(int re i=1;i<=logn[K];++i){
			for(int re j=1;(1<<i)+j-1<=K;++j){
				l[i][j]=min(l[i-1][j],l[i-1][j+(1<<(i-1))]);
				r[i][j]=min(r[i-1][j],r[i-1][j+(1<<(i-1))]);
			}
		}
	}
	
	inline ll query_l(cs int &L,cs int &R){
		int len=logn[R-L+1];
		return min(l[len][L],l[len][R-(1<<len)+1]);
	}
	
	inline ll query_r(cs int &L,cs int &R){
		int len=logn[R-L+1];
		return min(r[len][L],r[len][R-(1<<len)+1]);
	}
	
	inline int lower_bound(cs int &a){
		int l=1,r=K+1;
		while(l<r){
			int mid=(l+r)>>1;
			if(e[mid].to<a)l=mid+1;
			else r=mid;
		}
		return l;
	}
	
	inline int upper_bound(cs int &a){
		int l=1,r=K+1;
		while(l<r){
			int mid=(l+r)>>1;
			if(e[mid].to<=a)l=mid+1;
			else r=mid;
		} 
		return l;
	}
	
	inline ll Ql(int u,int v){
		u=max(1,u),v=min(v,n);
		u=lower_bound(u);
		v=upper_bound(v)-1;
		if(u>v)return 0x3f3f3f3f3f3f3f3f;
		return query_l(u,v);
	}
	
	inline ll Qr(int u,int v){
		u=max(1,u),v=min(v,n);
		u=lower_bound(u);
		v=upper_bound(v)-1;
		if(u>v)return 0x3f3f3f3f3f3f3f3f;
		return query_r(u,v);
	}
}

ll dist[N],ans;

inline bool check_L(int u,int pos){
	if(u==pos)return true;
	ll mpt=ST::Qr(u,u)-dist[pos];
	ll d1=ST::Qr(pos,u-1)-dist[pos];
	ll d2=ST::Ql(2*pos-u+1,pos)+dist[pos];
	if(d1<=mpt||d2<=mpt)return false;
	if(2*pos-u>0)return ST::Ql(2*pos-u,2*pos-u)+dist[pos]>mpt;
	return true;
}

inline bool check_R(int u,int pos){
	if(u==pos)return true;
	ll mpt=ST::Ql(u,u)+dist[pos];
	ll d1=ST::Ql(u+1,pos)+dist[pos];
	ll d2=ST::Qr(pos,2*pos-u-1)-dist[pos];
	if(d1<=mpt||d2<=mpt)return false;
	if(2*pos-u<=n)return ST::Qr(2*pos-u,2*pos-u)-dist[pos]>=mpt;
	return true;
}

inline int find_L(int u){
	int l=1,r=u;
	while(l<r){
		int mid=(l+r)>>1;
		if(check_L(u,mid))r=mid;
		else l=mid+1;
	}
	return l;
}

inline int find_R(int u){
	int l=u,r=n,ans=u;
	while(l<=r){
		int mid=(l+r+1)>>1;
		if(check_R(u,mid))l=mid+1,ans=mid;
		else r=mid-1;
	}
	return ans;
}

signed main(){
	n=getint(),m=getint();
	for(int re i=2;i<=n;++i)ST::logn[i]=ST::logn[i>>1]+1;
	for(int re i=2;i<=n;++i)dist[i]=dist[i-1]+getint();
	while(m--){
		K=getint();
		for(int re i=1;i<=K;++i)e[i].to=getint(),e[i].dis=getint();
		sort(e+1,e+K+1);
		for(int re i=1;i<=K;++i){
			ST::l[0][i]=e[i].dis-dist[e[i].to];
			ST::r[0][i]=e[i].dis+dist[e[i].to];
		}
		ST::init();
		ans=0;
		for(int re i=1;i<=K;++i)
		ans+=find_R(e[i].to)-find_L(e[i].to)+1;
		cout<<ans<<'\n';
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值