P7167 喷泉
感谢所有AC
思路
第 i 个圆盘中溢出的水会流到第一个编号大于 i 并且直径大于 i 盘的盘中,由于圆盘的直径固定,所以对于每一个圆盘,它的 next 圆盘是已经确定的。每一个圆盘的 next 圆盘可以用单调栈在线性的时间内求出,对于每一个询问,模拟水的流动,直到水流完为止,就可以确定水流到哪个盘中。
不难发现,上述方法的时间复杂度为 O(nq) 无法通过大数据。考虑到模拟水流过程中水是一个圆盘一个圆盘地流的,会产生大量重复计算,可以尝试用倍增的思想去优化水流过程的复杂度。定义状态 f[ i, t ] 表示从第 i 个圆盘开始,水溢出 2t 次后所到达的圆盘编号,有转移方程 f[ i, t ] = f[ f[ i, t-1 ], t-1 ] ,同时定义状态 g[ i, t ] 为水溢出所到达的共 2t 个圆盘的容量之和(不包括自身),有转移方程 g[ i, t ] = g[ i, t-1 ] + g[ f[ i, t ], t-1 ] 。
对于每一个询问,若溢出的水能够到达 2t 个盘,就变更初始盘为 f[x, t] ,继续判断剩下的水的流向,t 为递减的循环变量。循环结束后,水还可以再溢出一次,把 ans 更新为 f[ ans, 0 ]。
代码
#include<iostream>
#include<algorithm>
#define maxn 100007
using namespace std;
int N, Q, C[maxn], D[maxn], f[maxn][20], s[maxn];
long long g[maxn][20];
int main(void)
{
ios::sync_with_stdio(false);
cin >> N >> Q;
for (int i = 1; i <= N; i++)
cin >> D[i] >> C[i];
int top = 0; C[N + 1] = INT32_MAX;
for (int i = 1; i <= N; i++)
{
while(D[i] > D[s[top]] && top > 0)
{
f[s[top]][0] = i;
g[s[top--]][0] = C[i];
}
s[++top] = i;
}
while (top > 0) {
f[s[top]][0] = N + 1;
g[s[top--]][0] = C[N + 1];
}
for (int t = 1; t <= 16; t++)
for (int i = 1; i <= N; i++)
{
f[i][t] = f[f[i][t - 1]][t - 1];
g[i][t] = g[i][t - 1] + g[f[i][t - 1]][t - 1];
}
while (Q--) {
int R = 0, V = 0;
cin >> R >> V;
if (V > C[R]) {
V -= C[R];
for (int t = 16; t >= 0; t--)
{
if (V > g[R][t]) {
V -= g[R][t];
R = f[R][t];
}
}
R = f[R][0];
if (R == N + 1) R = 0;
}
cout << R << '\n';
}
return 0;
}

浙公网安备 33010602011771号