【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;
}
相关推荐
荼蘼2 分钟前
基于 KNN 算法的手写数字识别项目实践
人工智能·算法·机器学习
追风少年浪子彦2 分钟前
mybatis-plus实体类主键生成策略
java·数据库·spring·mybatis·mybatis-plus
赵英英俊4 分钟前
Python day26
开发语言·python
你怎么知道我是队长4 分钟前
python---eval函数
开发语言·javascript·python
溟洵7 分钟前
Qt 窗口 工具栏QToolBar、状态栏StatusBar
开发语言·前端·数据库·c++·后端·qt
Yuroo zhou22 分钟前
IMU的精度对无人机姿态控制意味着什么?
单片机·嵌入式硬件·算法·无人机·嵌入式实时数据库
铭哥的编程日记23 分钟前
《C++ list 完全指南:list的模拟实现》
c++
创码小奇客25 分钟前
Talos 使用全攻略:从基础到高阶,常见问题一网打尽
java·后端·架构
Rockson28 分钟前
期货实时行情接口接入教程
python·api
程序员编程指南40 分钟前
Qt 远程过程调用(RPC)实现方案
c语言·c++·qt·rpc·系统架构