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

假设我们把区间内的数排好序了,设为 a 1 ≤ a 2 ≤ ⋯ ≤ a n a_1\leq a_2\leq \cdots\leq a_n a1​≤a2​≤⋯≤an​。对于一个 u u u,它能吃完所有的人当且仅当:
∀ i < u , a u + ∑ j = 1 i − 1 a j − a i ≥ k ∀ i > u , ∑ j = 1 i − 1 a j − a i ≥ k \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} ∀i<u,au​+j=1∑i−1​aj​−ai​≥k∀i>u,j=1∑i−1​aj​−ai​≥k​
对于第一个条件 ∀ i < u , a u ≥ k + a i − ∑ j = 1 i − 1 a j \forall i<u,a_u\geq k+a_i-\sum_{j=1}^{i-1}a_j ∀i<u,au​≥k+ai​−∑j=1i−1​aj​,我们考虑维护出 a i − ∑ j = 1 i − 1 a j a_i-\sum_{j=1}^{i-1} a_j ai​−∑j=1i−1​aj​ 的前缀最值的位置(即类似一个单调栈的东西),那么对于同一段内的限制都是相同的。

注意到这样的前缀最值个数不是很多:设上一次前缀最值的位置为 l s t lst lst,那么 i i i 成为新的前缀最值需要满足 a i − ∑ j = 1 i − 1 a j > a l s t − ∑ j = 1 l s t − 1 a j → a i > a l s t + ∑ j = l s t i − 1 a j 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 ai​−∑j=1i−1​aj​>alst​−∑j=1lst−1​aj​→ai​>alst​+∑j=lsti−1​aj​,也就是说 a i a_i ai​ 至少会翻倍,那么前缀最值个数的一个上界为 O ( log ⁡ V ) O(\log V) O(logV)。

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

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

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

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

总时间复杂度 O ( n log ⁡ n + q log ⁡ V log ⁡ n ) O(n\log n+q\log V\log n) O(nlogn+qlogVlogn)。

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

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

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

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

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

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

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

#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 #2】史莱姆(值域分段)相关推荐

  1. 中石油训练赛 - 关于我转生变成史莱姆这档事(dfs)

    题目描述 关于我转生变成史莱姆这档事这部番剧中,上班族的三上悟因为某个事件而作为史莱姆在异世界转生了.在转生时得到了"大贤者"和"捕食者"这两个独特技能.虽然身 ...

  2. coreldraw登录老是出现未知错误_原神:新版本bug频出,史莱姆消失,联机模式提示506错误码...

    这个1.1版本真就"乐事"不断呀,各种bug频出,冻梨有些疑惑,版本上线前真的经过测试了么?不会就开发组跑一遍正常流程,通了就急匆匆地更新了吧. 绝大部分玩家都遭遇了的506错误代 ...

  3. android模拟器 diy,史莱姆机DIY模拟器

    史莱姆机DIY模拟器是一款非常治愈减压的手机游戏.该游戏有很多乐趣,它的玩法就类似与橡皮泥的玩法,不断你怎么捏它都是一个形状,该有戏是一款粘液模拟器,你可以在游戏里捏出各种各样的DIY出来,游戏画面和 ...

  4. 分裂的史莱姆(二进制)

    Description 史莱姆(Slime),是一种在现代电子游戏与奇幻小说常常出现的虚构生物,最早在1958年同名小说里出现.其流行形象是一种果冻状或半液体状.身体不透明或半透明.可以变换形状.能够 ...

  5. 史莱姆区块查找 超简单java代码

    现有的史莱姆区块查找器 打不开史莱姆区块查找器,自己写一个算了 步骤: 1.新建一个txt文件,复制下面代码到文件里并保存,然后文件名连同后缀改为 McCheckSlimeChunk.java 2.打 ...

  6. Java练习ArrayList的运用——勇者斗史莱姆

    现在有五个角色:勇者.法师.骑士.史莱姆.哥布林,他们各自具有的属性为姓名(name).生命值(health).基础攻击力(ATK). 首先,我们需要创建一个角色类(Role),为了方便日后查询角色状 ...

  7. 你听说过史莱姆吗??【oj】

    Description Input Output inpu 5 output 1 input 8 output 0 分析: 如果只有小Q一个人的话,就会有1,2,4,8,16,32,,,等情况,是以二 ...

  8. 基本算法的设计与实现-史莱姆通话

    题目描述 有n只史莱姆在数轴上站成一列,距离小于等于K的两只史莱姆之间可以相互通话.请问有多少对史莱姆之间能通话? 输入 两行,第一行,两个整数n和K,其中n<=500: 第二行,一个长度为n的 ...

  9. 史莱姆方块java_史莱姆 - Minecraft Wiki,最详细的官方我的世界百科

    史莱姆 大型:16( × 8) 中型:4( ) 小型:1() 行为 攻击型 攻击力 简单: 大型:3( ) 中型:2() 小型:0() 普通: 大型: 4( ) 中型: 2() 小型: 0() 困难: ...

最新文章

  1. 办公电脑选购方案指南
  2. Unet神经网络为什么会在医学图像分割表现好?
  3. @ngrx入坑angular的schema,爽的一逼!
  4. 时间 '2018-08-06T10:00:00.000Z' 格式转化为本地时间(转)
  5. 鸿蒙系统被泼冷水,给鸿蒙泼冷水:见不得同行的好,是人间最可恶的蠢和恶
  6. 浅说深度学习(1):核心概念
  7. JAVA正则表达式4种常用功能 [转]
  8. 前端浏览器兼容性网站
  9. 孙鑫VC学习笔记:第十六讲 (一) 利用事件对象实现线程间的同步
  10. 【MCM-2017】2017年数模美赛D题 - 特奖论文学习
  11. unable to find setter method for attribute:[commandName]
  12. 在shell中向应用程序的socket发送信息
  13. 统计相关系数(3)——Kendall Rank(肯德尔等级)相关系数及MATLAB实现
  14. GIS系列专题(2):Clipper计算机图形库使用说明(Vatti‘s clipping algorithm),类似刀具补偿
  15. panabit高级流控
  16. 使用ntsd命令强制性杀掉进程[微软未开公的密秘]
  17. ANC主动降噪耳机有哪些?ANC主动降噪耳机推荐!
  18. DNS和HTTP服务
  19. 三维地图之cesium轨迹回放(有代码)
  20. 将一个数的每一位都正序输出——简单算法

热门文章

  1. Javascript 声明时用“var”跟不用var的区别
  2. Python根据地名获取经纬度
  3. Ch11. Threads 线程
  4. LaTeX 如何安装一些非免费字体 getnonfreefonts
  5. 基于S3C2451的嵌入式电子相册
  6. 7-2 买电影票 (C语言)
  7. Frequency Estimation
  8. 高手速成android开源项目【View篇】
  9. 如何增加百度收录量和友好度
  10. Python|猜球博弈