洛谷 P7167 [eJOI 2020 Day1] Fountain(单调栈,ST表)

//第300篇博客祭QAQ

传送门


解题思路

挺好的一道题。

首先可以观察到,若按照水流方向建边,则整张图是个DAG。

  • 第一步:建图。
    令水池(0号店)为根。先用单调递减栈求出每个圆盘下面第一个比他大的圆盘,很显然水就往那里流,将这两个点之间连边,最后0号店向栈中剩下元素连边。
  • 第二步:预处理ST表
    设dp[i][j]表示从i号节点流2^j个圆盘所到达的圆盘。
    设f[i][j]表示从i号节点流2^j个圆盘需要总水量的下界(开区间),即只有v>f[i][j]时,才可以流过去。
  • 第三步:查询并得到答案
    需要注意:
    1. 开闭区间。
    2. 先更新剩余流量v,再所到达的节点r。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;
const int maxn=1e5+5;
stack<int> s;
struct Node{
	int d;
	long long c;
}a[maxn];
struct node{
	int v,next;
	long long w;
}e[maxn];
int dep[maxn],dp[maxn][35],cnt,p[maxn],n,q;
long long f[maxn][35];
void insert(int u,int v,int w){
	cnt++;
	e[cnt].v=v;
	e[cnt].next=p[u];
	e[cnt].w=w;
	p[u]=cnt;
}
void dfs(int u,int fa,int deep,long long w){
	dep[u]=deep;
	dp[u][0]=fa;
	f[u][0]=w;
	for(int j=1;j<=30;j++){
		if((1<<j)>=dep[u]) break;
		dp[u][j]=dp[dp[u][j-1]][j-1];
		f[u][j]=f[u][j-1]+f[dp[u][j-1]][j-1];
	}
	for(int i=p[u];i!=-1;i=e[i].next){
		int v=e[i].v;
		dfs(v,u,deep+1,e[i].w);
	}
} 
int main(){
	ios::sync_with_stdio(false);
	memset(p,-1,sizeof(p));
	memset(f,0x3f,sizeof(f));
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>a[i].d>>a[i].c;
		while(!s.empty()&&a[s.top()].d<a[i].d){
			insert(i,s.top(),a[s.top()].c);
			s.pop();
		}
		s.push(i);
	}
	while(!s.empty()){
		insert(0,s.top(),a[s.top()].c);
		s.pop();
	}
	dfs(0,0,1,1e10+5);
	while(q--){
		int r;
		long long v;
		cin>>r>>v;
		for(int j=log2(dep[r]-1);j>=0;j--){
			if(f[r][j]<v){
				v-=f[r][j];
				r=dp[r][j];
			}
		}
		cout<<r<<endl;
	}
	return 0;
}
posted @ 2021-09-24 11:09  尹昱钦  阅读(124)  评论(0编辑  收藏  举报