洛谷 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]时,才可以流过去。 - 第三步:查询并得到答案
需要注意:- 开闭区间。
- 先更新剩余流量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;
}