【ST表、倍增】P7167 [eJOI 2020] Fountain (Day1)

题目描述

大家都知道喷泉吧?现在有一个喷泉由 NNN 个圆盘组成,从上到下以此编号为 1∼N1 \sim N1∼N,第 iii 个喷泉的直径为 DiD_iDi,容量为 CiC_iCi,当一个圆盘里的水大于了这个圆盘的容量,那么水就会溢出往下流,直到流入半径大于这个圆盘的圆盘里。如果下面没有满足要求的圆盘,水就会流到喷泉下的水池里。

现在给定 QQQ 组询问,每一组询问这么描述:

  • 向第 RiR_iRi 个圆盘里倒入 ViV_iVi 的水,求水最后会流到哪一个圆盘停止。

如果最终流入了水池里,那么输出 000。

注意,每个询问互不影响。

输入格式

第一行两个整数 N,QN,QN,Q 代表圆盘数和询问数。

接下来 NNN 行每行两个整数 Di,CiD_i,C_iDi,Ci 代表一个圆盘。

接下来 QQQ 行每行两个整数 Ri,ViR_i,V_iRi,Vi 代表一个询问。

输出格式

QQQ 行每行一个整数代表询问的答案。

对于 100%100\%100% 的数据:

  • 2≤N≤1052 \le N \le 10^52≤N≤105。
  • 1≤Q≤2×1051 \le Q \le 2 \times 10^51≤Q≤2×105。
  • 1≤Ci≤10001 \le C_i \le 10001≤Ci≤1000。
  • 1≤Di,Vi≤1091 \le D_i,V_i \le 10^91≤Di,Vi≤109。
  • 1\\le R_i \\le N

思路

可以将水池看作第 n+1n + 1n+1 个圆盘,直径和容量都是无穷大。

考虑将题目划分成2部分:如何求直径大于自己的下一个圆盘,如何求出一共需要经过几次下一个圆盘才能把水装满。

对于第一部分,可以先用 ST 表求出区间最大值。不妨设当前圆盘是第 iii 个,容易发现一定存在一个最小的 jjj 使 max⁡k=i+1jDk>Di\max^{j}{k = i+1}D_k > D_imaxk=i+1jDk>Di,且当 P>jP>jP>j 时都有 max⁡k=i+1PDk≥max⁡k=i+1jDk>Di\max^{P}{k = i+1}D_k \geq \max^{j}_{k = i+1}D_k> D_imaxk=i+1PDk≥maxk=i+1jDk>Di 成立,因此满足单调性,可以用二分来求出这个 jjj。ST 表预处理 O(NlogN)O(NlogN)O(NlogN),单次查询 O(1)O(1)O(1)。对于 nnn 个圆盘二分,复杂度也是 O(NlogN)O(NlogN)O(NlogN),总复杂度 O(NlogN)O(NlogN)O(NlogN)。

对于第二部分。如果对于每次查询都一个一个往下一个圆盘跳,则当圆盘大小单调递增的时候,总复杂度可能达到 O(nQ)O(nQ)O(nQ),无法接受。考虑用倍增的方法,一次往下跳 2k2^k2k 个,kkk 同理也可以二分求出,总复杂度还是 O(QlogN)O(QlogN)O(QlogN),常数稍微大一点。

代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,nex[100005][25],add[100005][25],maxx[100005][25];
int d[100005],c[100005];
//l 到 r 之间区间最大直径 
int ask_max(int l,int r) {
	int t = 0;
	for(;(1 << t) <= (r - l + 1);) {
		t++;
	}
	t--; 
	return max(maxx[l][t],maxx[r - (1 << t) + 1][t]);
}
signed main() {
	scanf("%lld %lld",&n,&q);
	for(int i = 1;i <= n;i++) {
		scanf("%lld %lld",&d[i],&c[i]);
		maxx[i][0] = d[i]; 
	}
	d[n + 1] = 1000000000005,c[n + 1] = 10000000000005;
	for(int i = 1;(1 << i) <= n + 1;i++) {
		for(int j = 1;j + (1 << i) - 1 <= n + 1;j++) {
			maxx[j][i] = max(maxx[j][i - 1],maxx[j + (1 << (i - 1))][i - 1]);
		}
	}
	for(int i = 1;i <= n;i++) {
		int l = i + 1,r = n + 1,mid;
		while(l < r) {
			mid = (l + r) >> 1;
			if(ask_max(i + 1,mid) > d[i]) r = mid;
			else l = mid + 1;
		}
		nex[i][0] = i,add[i][0] = c[i];
		nex[i][1] = l,add[i][1] = c[l] + c[i];
	}
	for(int i = 0;i <= 24;i++) nex[n + 1][i] = n + 1,add[n + 1][i] = c[n + 1];
	for(int i = 2;i <= 20;i++) {
		for(int j = 1;j <= n;j++) {
			nex[j][i] = nex[nex[nex[j][i - 1]][1]][i - 1];
			add[j][i] = add[j][i - 1] + add[nex[nex[j][i - 1]][1]][i - 1];
		}
	}
	while(q--) {
		int now,v;
		scanf("%lld %lld",&now,&v);
		while(v) {
			if(v <= c[now]) {
				if(now != n + 1) printf("%lld\n",now);
				else printf("0\n");
				break;
			}
			int l = 0,r = 20,mid;
			while(l < r - 1) {
				mid = (l + r) >> 1;
				if(add[now][mid] >= v) r = mid - 1;
				else l = mid; 
			}		
			v -= add[now][l];
			now = nex[nex[now][l]][1];
		} 
		
	}
    return 0;
}
相关推荐
王小王-12315 小时前
基于Django的个性化餐饮场所推荐系统
后端·python·django·个性化餐厅推荐·个性化餐饮推荐
-森屿安年-15 小时前
62. 不同路径
算法·动态规划
TeamDev15 小时前
JxBrowser 9.1.2 版本发布啦!
java·跨平台·混合应用·jxbrowser·浏览器控件·compose 多平台
逢君学术论文AI写作15 小时前
Java第21课:JavaWeb入门——Tomcat+第一个Servlet
java·servlet·tomcat
Wonderful U15 小时前
Python+Django实战|个人&家庭记账理财系统:多账户管理、收支分类、日常记账、预算管控、账单检索、数据可视化、报表导出
python·信息可视化·django
就叫_这个吧15 小时前
Java使用tomcat+servlet+filter实现简单的登录功能,需先登录再进行页面数据管理操作
java·开发语言·servlet·tomcat·jsp·filter
FirstFrost --sy15 小时前
基于高并发服务器的web小游戏测试
服务器·前端·javascript·c++·python·集成测试
abcy07121316 小时前
在Python 中使用Celery和Kafka进行消息队列的生产者和消费者实现
python·kafka
学计算机的计算基16 小时前
Codex CLI vs Claude Code 全方位对比:设计哲学与用户体验深度解析
算法
川石课堂软件测试16 小时前
UI自动化测试|下拉选择框&弹出框&滚动条操作实践
开发语言·python·jmeter·ui·docker·单元测试·harmonyos