【PR #2】史莱姆(值域分段)

首先看单次询问我们怎么做。对于一个人,他的最优策略显然是不断吃最小的,并看最后能不能吃完。

假设我们把区间内的数排好序了,设为 \(a_1\leq a_2\leq \cdots\leq a_n\)。对于一个 \(u\),它能吃完所有的人当且仅当:

\[\begin{aligned} \forall i<u,a_u+\sum_{j=1}^{i-1}a_j-a_i\geq k\\ \forall i>u,\sum_{j=1}^{i-1}a_j-a_i\geq k \end{aligned} \]

对于第一个条件 \(\forall i<u,a_u\geq k+a_i-\sum_{j=1}^{i-1}a_j\),我们考虑维护出 \(a_i-\sum_{j=1}^{i-1} a_j\) 的前缀最值的位置(即类似一个单调栈的东西),那么对于同一段内的限制都是相同的。

注意到这样的前缀最值个数不是很多:设上一次前缀最值的位置为 \(lst\),那么 \(i\) 成为新的前缀最值需要满足 \(a_i-\sum_{j=1}^{i-1}a_j>a_{lst}-\sum_{j=1}^{lst-1}a_j\to a_i>a_{lst}+\sum_{j=lst}^{i-1}a_j\),也就是说 \(a_i\) 至少会翻倍,那么前缀最值个数的一个上界为 \(O(\log V)\)

但直接找到前缀最值是困难的,不过发现我们可以放宽一点限制:我们可以找到 \(a_i-\sum_{j=1}^{i-1}a_j\) 上升的位置,即 \(a_i-\sum_{j=1}^{i-1}a_j>a_{i-1}-\sum_{j=1}^{i-2}a_j\to a_i>2a_{i-1}\)。满足这个条件的 \(a_i\) 同样只有至多 \(O(\log V)\) 个,而且可以用主席树挨个求出。

第二个条件是与 \(u\) 无关的,那么我们可以先找到最大的 \(p\) 满足 \(\sum_{j=1}^{p-1}a_j-a_p<k\),那么对于任意 \(u<p\) 它们都不是答案,对于任意 \(u\geq p\) 我们只需关注第一个条件。

发现在解决第一个条件的过程中,我们已经找到了 \(a_i-\sum_{j=1}^{i-1}a_j\) 上升的所有位置,而我们要找的是满足 \(a_p-\sum_{j=1}^{p-1}a_j>-k\) 的最大的 \(p\),于是我们就已经可以锁定 \(p\) 在哪两个上升位置之间,而这两个位置之间的 \(a_i-\sum_{j=1}^{i-1}a_j\) 是单降的,所以可以通过二分求 \(p\)

而对于一个前缀最值段内,对 \(a_u\) 的限制都是一样的,容易用主席树求出合法的 \(u\) 的个数。

总时间复杂度 \(O(n\log n+q\log V\log n)\)

接下来介绍蒋老师的单 \(\log\) 做法:

首先应注意到单调性,即若 \(i\) 最后能获胜,则 \(\geq i\) 的人最后也一定能获胜。这样我们只需要找第一个能获胜的人 \(u\)

用的是值域分段的技巧,我们将 \([2^b,2^{b+1})\) 分成一段。那么仍然考虑 \(u\) 能吃完所有人的条件,发现:

  • 对于非 \(u\) 所在的段,只要 \(u\) 能够吃掉该段的段头(出现在该段中的最小的数),\(u\) 就能吃掉这一整段
  • 对于 \(u\) 所在的段:若 \(u\) 不是段头,则条件和上一条相同;若 \(u\) 是段头,只要 \(u\) 能够吃掉该段第二个数,\(u\) 就能吃掉这一整段。

同样,对于 \(u\) 后面的那些段,能吃掉段头的限制是和 \(u\) 无关的。那么我们可以先找到最后一个吃不掉段头的段,设为第 \(T\) 段,那么 \(u\) 就必须在第 \(T\) 段及之后,且此时我们无需再考虑 \(u\) 之后的段的限制。

那么我们考虑从前往后枚举 \(u\) 所在的段,对于 \(u\) 为段头的情况我们单独考虑,对于 \(u\) 非段头的情况,根据前面的段和当前段,我们会得到一个形如 \(a_u\geq x\) 的限制,此时只需要看该段中最大的数是否 \(\geq x\):若 \(\geq x\),则能获胜的人就是所有 \(\geq x\) 的人;若 \(<x\),则继续考虑 \(u\) 是否在下一段。

我们只需要支持一些主席树上的操作,以及对每一段的区间求和、区间 min 和次 min、区间 max,使用 st 表即可做到 \(O((n+q)(\log n+\log V))\)

#include<bits/stdc++.h>

#define N 200010
#define ll long long
#define INF 0x7fffffff
#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

const int B=30;
inline int hb(int x){return 31-__builtin_clz(x);}

int n,q,a[N];

namespace Seg
{
	#define lc(u) ch[u][0]
	#define rc(u) ch[u][1]
	const int nn=1e9;
	const int NN=10000000;
	int node,rt[N],ch[NN][2],size[NN];
	inline void update(int &u,int lst,int l,int r,int x)
	{
		u=++node,lc(u)=lc(lst),rc(u)=rc(lst),size[u]=size[lst]+1;
		if(l==r) return;
		int mid=(l+r)>>1;
		if(x<=mid) update(lc(u),lc(lst),l,mid,x);
		else update(rc(u),rc(lst),mid+1,r,x);
	}
	inline int query(int a,int b,int l,int r,int x)
	{
		if(x<=l) return size[b]-size[a];
		int mid=(l+r)>>1,ans=0;
		if(x<=mid) ans+=query(lc(a),lc(b),l,mid,x);
		return ans+query(rc(a),rc(b),mid+1,r,x);
	}
	void init()
	{
		for(int i=1;i<=n;i++)
			update(rt[i],rt[i-1],1,nn,a[i]);
	}
	int query(int l,int r,int x){return query(rt[l-1],rt[r],1,nn,x);}
	#undef lc
	#undef rc
}

struct data
{
	pii min1,min2;
	data(){}
	data(int p){min1=mk(a[p],p),min2=mk(INF,114514);}
	data(pii x,pii y){min1=x,min2=y;}
};

inline data max(const data &a,const data &b)
{
	if(a.min1<b.min1) return data(a.min1,min(a.min2,b.min1!=a.min1?b.min1:b.min2));
	return data(b.min1,min(b.min2,a.min1!=b.min1?a.min1:a.min2));
}

template <class T> T Set(int p);
template<> int Set<int>(int p){return a[p];}
template<> data Set<data>(int p){return data(p);}

template<class T>
struct ST
{
	vector<vector<T>> maxn;
	void init(const vector<int> &pos)
	{
		const int nn=pos.size();
		maxn.resize(nn);
		for(int i=nn-1;i>=0;i--)
		{
			maxn[i].resize(hb(nn-i)+1);
			maxn[i][0]=Set<T>(pos[i]);
			for(int j=0;i+(1<<(j+1))-1<nn;j++)
				maxn[i][j+1]=max(maxn[i][j],maxn[i+(1<<j)][j]);
		}
	}
	inline T query(int l,int r)
	{
		const int b=hb(r-l+1);
		return max(maxn[l][b],maxn[r-(1<<b)+1][b]);
	}
};

struct block
{
	int nn,lef[N],rig[N];
	vector<int> pos;
	ST<int> maxn;
	ST<data> minn;
	vector<ll> sum;
	void init()
	{
		nn=pos.size();
		static int vis[N];
		memset(vis,-1,sizeof(vis));
		for(int i=0;i<nn;i++) vis[pos[i]]=i;
		for(int i=1,lst=-1;i<=n;lef[i]=lst,i++) if(~vis[i]) lst=vis[i];
		for(int i=n,lst=nn;i>=1;rig[i]=lst,i--) if(~vis[i]) lst=vis[i];
		maxn.init(pos),minn.init(pos);
		sum.resize(nn);
		for(int i=0;i<nn;i++) sum[i]=a[pos[i]]+(i?sum[i-1]:0);
	}
	inline bool empty(int l,int r){return rig[l]>lef[r];}
	inline int querymax(int l,int r){return maxn.query(rig[l],lef[r]);}
	inline data querymin(int l,int r){return minn.query(rig[l],lef[r]);}
	inline ll querysum(int l,int r){return sum[lef[r]]-(rig[l]?sum[rig[l]-1]:0);}
}b[B];

int query(int l,int r,int k)
{
	int T=0;
	ll sum=0;
	for(int i=0;i<B;i++)
	{
		if(b[i].empty(l,r)) continue;
		int head=b[i].querymin(l,r).min1.fi;
		if(sum<head+k) T=i;
		sum+=b[i].querysum(l,r);
	}
	ll premax=-1e15; sum=0;
	for(int i=0;i<T;i++)
	{
		if(b[i].empty(l,r)) continue;
		int head=b[i].querymin(l,r).min1.fi;
		premax=max(premax,head-sum);
		sum+=b[i].querysum(l,r);
	}
	int X=-1;
	for(int i=T;i<B;i++)
	{
		if(b[i].empty(l,r)) continue;
		data minn=b[i].querymin(l,r);
		int head=minn.min1.fi,sec=minn.min2.fi;
		if(sec==INF)
		{
			if(head>=premax+k)
			{
				X=head;
				break;
			}
			premax=max(premax,head-sum);
			sum+=head;
			continue;
		}
		if(head>=premax+k&&sum+head>=sec+k)
		{
			X=head;
			break;
		}
		premax=max(premax,head-sum);
		sum+=b[i].querysum(l,r);
		int maxn=b[i].querymax(l,r);
		if(maxn>=premax+k)
		{
			X=max(sec,(int)premax+k);
			break;
		}
	}
	if(X==-1) return 0;
	return Seg::query(l,r,max(X,1));
}

int main()
{
	n=read(),q=read();
	for(int i=1;i<=n;i++) 
		a[i]=read(),b[hb(a[i])].pos.push_back(i);
	for(int i=0;i<B;i++) b[i].init();
	Seg::init();
	while(q--)
	{
		int l=read(),r=read(),k=read();
		printf("%d\n",query(l,r,k));
	}
	return 0;
}
/*
6 4
3 1 5 3 7 5
4 6 4
*/
/*
3 2
3 3 3
1 3 1
1 3 0
*/
posted @ 2022-10-29 11:23  ez_lcw  阅读(30)  评论(0编辑  收藏  举报