首先看单次询问我们怎么做。对于一个人,他的最优策略显然是不断吃最小的,并看最后能不能吃完。
假设我们把区间内的数排好序了,设为 \(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
*/
标签:PR,log,int,值域,sum,史莱姆,lst,maxn,premax
From: https://www.cnblogs.com/ez-lcw/p/16838358.html