Gym 102091A: Flying Squirrel(RMQ)
题意:如图,有N个柱子,每次我可以从高柱子X到低柱子Y,而且需要满足中间的柱子都小于X的高度。
思路:现在有Q次询问,每次给定(X,Y),(如果ht[X]<ht[Y],则交换XY),问X为起点,Y为终点的最长路径。 如果Y为0,你可以选择任一点为终点。
每次我们把当前dfs的区间最高的几个柱子(假设高度为H)抽出来,它们把当前区间划分为了几个小区间,可以把这些高的柱子看成根,那么被夹在中间的区间就是子树,再去dfs深入中间的区间即可。 最后假如要从X到Y,如果它们之间没有更高的,答案就是深度差。
由于每个柱子只被当成一次根,所的复杂度是O(N)的,加上RMQ找区间最大值,总的时间是O(NlogN)的。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; int h[maxn],dep[maxn],ans[maxn]; int mx[20][maxn],Log[maxn]; int Max(int a,int b){ return h[a]>=h[b]?a:b;} int find(int L,int R) { int k=Log[R-L+1]; return Max(mx[k][L],mx[k][R-(1<<k)+1]); } int solve(int L,int R,int d) { if(L>R) return -1; int p=find(L,R); dep[p]=d; ans[p]=solve(L,p-1,d+1)+1; int res=ans[p]; while(p<R){ int q=find(p+1,R); if(h[q]!=h[p]) break; dep[q]=d; int t=solve(p+1,q-1,d+1)+1; res=max(res,t); ans[p]=max(ans[p],t); ans[q]=t; p=q; } ans[p]=max(ans[p],solve(p+1,R,d+1)+1); res=max(res,ans[p]); return res; } int main() { int N,M,L,R; scanf("%d%d",&N,&M); rep(i,1,N) scanf("%d",&h[i]); Log[0]=-1; rep(i,1,N) Log[i]=Log[i>>1]+1; rep(i,1,N) mx[0][i]=i; for(int i=1;(1<<i)<=N;i++) for(int j=1;j+(1<<i)-1<=N;j++) mx[i][j]=Max(mx[i-1][j],mx[i-1][j+(1<<(i-1))]); solve(1,N,0); rep(i,1,M){ scanf("%d%d",&L,&R); if(h[L]<h[R]) swap(L,R); if(!R) printf("%d\n",ans[L]); else { if(L==R) puts("0"); else { int p; if(L<R) p=find(L+1,R); else p=find(R,L-1); if(h[p]>=h[L]) puts("0"); else printf("%d\n",dep[R]-dep[L]); } } } return 0; }
It is your time to fight!