!-- Loading 底层遮罩 -->

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;
}

 

posted @ 2022-03-31 18:40  Thinker-X  阅读(60)  评论(0)    收藏  举报