洛谷 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章