题目描述
大家都知道喷泉吧?现在有一个喷泉由 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 使 maxk=i+1jDk>Di\max^{j}{k = i+1}D_k > D_imaxk=i+1jDk>Di,且当 P>jP>jP>j 时都有 maxk=i+1PDk≥maxk=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;
}