cf500e New Year Domino


\(n\) 个多米诺骨牌,从左到右排列,每一个骨牌都有一个高度 \(L_i\),向右推倒,它会直接向右倒下,如下图,倒下后该骨牌的顶端落在 \(X_i+L_i\) 的位置, ( \(X_i\) 是它位于的坐标,即倒下时该骨牌不会发生移动)

在倒下过程中,骨牌会碰到其他骨牌,碰到的骨牌会向右倒,如下图,最左边的骨牌倒下会碰倒 \(A,B,C\)\(A,B,C\) 会倒下,但是不会直接碰到 \(D\) ,但是 \(D\) 会因为 \(C\) 的倒下而碰倒。

现在给你N个骨牌的坐标 \(X_i\) ,和每个骨牌的高度 \(L_i\) 。则一个骨牌能碰倒另一个骨牌当切仅当 \(X_i+l_i\geq x_j\)。同时有 \(Q\) 个询问 \([L,R]\) ,问向右推到第 \(L\) 个骨牌,最少需要多少代价让 \(R\) 倒下。你可以临时增加某个骨牌的高度,增加 \(1\) 个高度的代价是 \(1\) .

\(2\leq n\leq 2\cdot 10^5,1\leq P_i,L_i\leq 10^9,1\leq q\leq 2\cdot 10^5,1\leq l\leq r\leq n\)

推到第 \(i\) 个骨牌,能倒下的最后一个骨牌的下一个是 \(nxt(i)\) ,发现 \(i\to nxt(i)\) 连边会变成一些森林.

对于一些骨牌连通块,考虑如果想要推到第 \(i+1\) 个骨牌,增高第 \(i\) 个骨牌的高度最优.

上述两个性质,就可以倍增了.

这题的代码也非常难写呢. 首先,倍增的答案计算细节很多,其次,考虑计算 \(nxt(i)\) 时,是 \(i+1\)\(P_j 的最后一个 \(j\) 中间的 \(nxt\) 的最大值,而不是 \(nxt(j)\) ,因为 \(nxt\) 不单调递增.

时间复杂度 : \(O(n\log n)\)

空间复杂度 : \(O(n\log n)\)

code

#include
using namespace std;
char in[100005];
int iiter=0,llen=0;
inline char get(){
	if(iiter==llen)llen=fread(in,1,100000,stdin),iiter=0;
	if(llen==0)return EOF;
	return in[iiter++];
}
inline int rd(){
	char ch=get();while(ch<'0'||ch>'9')ch=get();
	int res=0;while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+ch-'0',ch=get();
	return res;
}
inline void pr(long long res){
	if(res==0){putchar('0');return;}
	static int out[20];int len=0;
	while(res)out[len++]=res%10,res/=10;
	for(int i=len-1;i>=0;i--)putchar(out[i]+'0');
}
#define MP make_pair
#define FI first
#define SE second
const int N=2e5+10;
class SGT{
public:
	int ts[N<<2];
	void init(){memset(ts,0,sizeof(ts));}
	void upd(int x,int l,int r,int pos,int val){
		if(l==r){
			ts[x]=max(ts[x],val);
			return;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)upd(x<<1,l,mid,pos,val);
		else upd(x<<1|1,mid+1,r,pos,val);
		ts[x]=max(ts[x<<1],ts[x<<1|1]);
	}
	int qry(int x,int l,int r,int cl,int cr){
		if(l==cl&&r==cr)return ts[x];
		int mid=(l+r)>>1;
		if(cr<=mid)return qry(x<<1,l,mid,cl,cr);
		else if(mid+1<=cl)return qry(x<<1|1,mid+1,r,cl,cr);
		else return max(qry(x<<1,l,mid,cl,mid),qry(x<<1|1,mid+1,r,mid+1,cr));
	}
}T1,T2;
int n,q;
int P[N],L[N],R[N];
pairnxt[20][N];
long long qry(int l,int r){
	int tmp=l;
	long long cost=0,res=nxt[19][l].FI;
	for(int k=19;k>=0;k--){
		if(r<=R[tmp]){
			res=min(res,cost);
			break;
		}	
		if(r<=R[nxt[k][tmp].SE])res=min(res,cost+nxt[k][tmp].FI);
		else cost+=nxt[k][tmp].FI,tmp=nxt[k][tmp].SE;
	}
	if(r<=R[tmp])res=min(res,cost);
	return res;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	n=rd();
	for(int i=0;i=0;i--){
		int pos=upper_bound(P,P+n,P[i]+L[i])-P-1;
		nxt[0][i].SE=pos>i?T2.qry(1,0,n-1,i+1,pos):i+1;
		T2.upd(1,0,n-1,i,nxt[0][i].SE);
		nxt[0][i].FI=nxt[0][i].SE==n?0:P[nxt[0][i].SE]-T1.qry(1,0,n-1,i,nxt[0][i].SE-1);
		R[i]=nxt[0][i].SE-1;
	}
	for(int k=0;k+1<20;k++){
		for(int i=0;i