HDU 4123(两种方法-RMQ,单调队列)
题意:50000个点的树,每个点有一个人,每个人会跑到离自己初始点距离最远的点上,这个距离为distance[i]。给你500个查询,对于每个查询Q,找一段连续编号的人,比如[left,right],满足 max( distance[i] i∈[left,right] ) – min( distance[i] i∈[left,right] ) ≤ Q,并且使得length=right-left+1要最大,求这个最大的length
法一:关键是O(1)的RMQ。。。。
分析:求distance[i]数组(代码中的d),可以用搜边的方法来解决,然后线性维护一个队列,使得队列中的最大值 – 最小值 ≤ Q,之后,会有一个新的distance[i]插到队列的头部,这样会打破“最大值 – 最小值 ≤ Q”的规则,此时要从队列末尾弹出元素,直到重新满足规则。很明显,维护队列的时候要不断查询某一区间的最大值和最小值,这个可以用RMQ来解决。对于每个distance[i],最多进队列和出队列一次,所以总复杂度为O(nlogn+mn),这貌似有点大啊。
此题还必须有一个极端的优化(当时赛后才想到这个,悲剧。):由于维护队列的时候要不断查询某一区间的最大值和最小值,而log函数慢的一比,所以,要预处理log数组。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdlib> #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<map> #include<list> #include<queue> #include<stack> #include<vector> #define tree int o,int l,int r #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define lo o<<1 #define ro o<<1|1 #define pb push_back #define mp make_pair #define ULL unsigned long long #define LL long long #define inf 0x7fffffff #define eps 1e-7 #define N 50009 using namespace std; int m,n,T,t,x,y,z; vector<pair<int,int> >g[N]; int f[N],s[N],sub[N]; int minv[N][17],maxv[N][17]; int lg[N]; void init() { for (int i=0; i<=n; ++i ) g[i].clear(); } void dfs1(int u,int fa) { f[u]=s[u]=0; for(int i=0; i<g[u].size(); i++) { int v=g[u][i].first; int l=g[u][i].second; if(v!=fa) { dfs1(v,u); if(f[u]<f[v]+l) { s[u]=f[v]+l; swap(s[u],f[u]); sub[u]=v; } else if(s[u]<f[v]+l) { s[u]=f[v]+l; } } } } void dfs2(int u,int fa) { for(int i=0; i<g[u].size(); i++) { int v=g[u][i].first; int l=g[u][i].second; if(v!=fa) { int val; if(sub[u]==v) val=s[u]; else val=f[u]; val+=l; if(f[v]<val) { s[v]=val; swap(s[v],f[v]); sub[v]=u; } else if(s[v]<val) { s[v]=val; } dfs2(v,u); } } } void rmqinit() { for(int i=1; i<=n; i++) minv[i][0]=maxv[i][0]=f[i]; for(int j=1; (1<<j)<=n; j++) for(int i=1; i+(1<<j)-1<=n; i++) { minv[i][j]=min(minv[i][j-1],minv[i+(1<<(j-1))][j-1]); maxv[i][j]=max(maxv[i][j-1],maxv[i+(1<<(j-1))][j-1]); } } int rmqx(int l,int r) { int len=lg[r-l+1]; return min(minv[l][len],minv[r-(1<<len)+1][len]); } int rmqd(int l,int r) { int len=lg[r-l+1]; return max(maxv[l][len],maxv[r-(1<<len)+1][len]); } int main() { #ifndef ONLINE_JUDGE freopen("ex.in","r",stdin); #endif for(int i=1,j=0; i<N; i++)//预处理使RMQ的复杂度变为O(1) { if(i<=(1<<(j+1))) lg[i]=j; else lg[i]=++j; } while(scanf("%d%d%*c",&n,&m)==2) { if(!n&&!m)return 0; init(); for (int i=0; i<n-1; ++i ) { scanf("%d%d%d",&x,&y,&z); g[x].pb(mp(y,z)); g[y].pb(mp(x,z)); } dfs1(1,-1); dfs2(1,-1); rmqinit();//O(nlgn) while(m--)//O(mn) { int q,ans=1; scanf("%d",&q); int d=0,x=inf,j=1; for(int i=1; i<=n; i++) { d=max(d,f[i]); x=min(x,f[i]); if(d-x>q) { j++; int xiao=rmqx(j,i);//O(1) int da=rmqd(j,i); while(da-xiao>q)//j==i时会停 { j++; xiao=rmqx(j,i); da=rmqd(j,i); } d=da;/////////// x=xiao; } ans=max(ans,i-j+1); } printf("%d\n",ans); } } return 0; }
法二:两个单调队列(一个维护最大,一个维护最小)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdlib> #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<map> #include<list> #include<queue> #include<stack> #include<vector> #define tree int o,int l,int r #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define lo o<<1 #define ro o<<1|1 #define pb push_back #define mp make_pair #define ULL unsigned long long #define LL long long #define inf 0x7fffffff #define eps 1e-7 #define N 50009 using namespace std; int m,n,T,t,x,y,z; vector<pair<int,int> >g[N]; int f[N],s[N],sub[N]; int qs[N],qj[N]; void init() { for (int i=0; i<=n; ++i ) g[i].clear(); } void dfs1(int u,int fa) { f[u]=s[u]=0; for(int i=0; i<g[u].size(); i++) { int v=g[u][i].first; int l=g[u][i].second; if(v!=fa) { dfs1(v,u); if(f[u]<f[v]+l) { s[u]=f[v]+l; swap(s[u],f[u]); sub[u]=v; } else if(s[u]<f[v]+l) { s[u]=f[v]+l; } } } } void dfs2(int u,int fa) { for(int i=0; i<g[u].size(); i++) { int v=g[u][i].first; int l=g[u][i].second; if(v!=fa) { int val; if(sub[u]==v) val=s[u]; else val=f[u]; val+=l; if(f[v]<val) { s[v]=val; swap(s[v],f[v]); sub[v]=u; } else if(s[v]<val) { s[v]=val; } dfs2(v,u); } } } int main() { #ifndef ONLINE_JUDGE freopen("ex.in","r",stdin); #endif while(scanf("%d%d%*c",&n,&m)==2) { if(!n&&!m)return 0; init(); for (int i=0; i<n-1; ++i ) { scanf("%d%d%d",&x,&y,&z); g[x].pb(mp(y,z)); g[y].pb(mp(x,z)); } dfs1(1,-1); dfs2(1,-1); while(m--)//O(mn) { int q,ans=1; scanf("%d",&q); int sl=0,sr=0,jl=0,jr=0,temp=0; for(int i=1; i<=n; i++) { while(sl<sr&&f[qs[sr-1]]>=f[i])sr--; while(jl<jr&&f[qj[jr-1]]<=f[i])jr--; qs[sr++]=i; qj[jr++]=i; while(f[qj[jl]]-f[qs[sl]]>q)//对于右区间i,找到左区间! { if(qj[jl]<qs[sl]) temp=max(temp,qj[jl++]); else temp=max(temp,qs[sl++]); } ans=max(ans,i-temp); } printf("%d\n",ans); } } return 0; }