hdu-6406 线段树+二分
Posted king-of-dark
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu-6406 线段树+二分相关的知识,希望对你有一定的参考价值。
这个题我觉得题意给你很明显了,就是让你判断这一个改变之后如何让前面和后面接上,熟悉的朋友很容易感觉出这个经典问题,最大值不断变化的值要是在一个数轴上画图像的话,是一个阶梯的形状,所以这个点抬高或降低就有很直观的影响了。
1.对于这个点被降低:如果这个点不在阶梯的转角上,那么这个点没有任何意义,答案不变,如果在转角上,你要判断这个点下降后前面的最大值现在是多少(设为t),对后续产生了多少的影响,你现在要知道这个点之后的第一个大于t的值在哪即可接的上。
2.对于这个点被抬高:如果这个点不在阶梯的转角上,还是像后查询第一个大于它的位置就行了,如果这个点不在转角上,但是有突破了阶梯的可能,这个时候前缀的最大值还是变了,查询第一和大于新最大值的位置同理。
现在还要做的事情有两件:计算从i开始选能选的数量,如何在1p+1~n中查询第一个大于t的位置。
第一个问题:其实你是可以从后往前维护一个单调栈即可,往里压的时候必须是降序即可。(具体看代码理解比较好一点)
第二个问题:用线段树维护区间最大值,然后二分就行了。
总结:这道题思路来的很快,先是乱写了两发,根本情况想的非常天真,我太蠢了,乱交。后来预处理后缀居然写了一个线段树+离散的,明明如此复杂,能解决这个问题的方法不止一种,那么我为什么选了一个如此复杂的狗东西呢,下次多想一想不行吗?还有代码出现了bug,不知道是不是状态问题,一个小细节错了,查了好久。最后一个大傻逼bug是我没有计算上什么都没有这种情况的后缀,真是太傻了,思路还是不严谨。
期望更改方案:正常估计比较难的题多想一想,别脑子一热就写(感觉cf打多,上来一顿胡猜,但是难题别自以为是啊),方法太复杂的时候别觉得自己就是对了,多动脑想一想其他的会死人?还有下次查错,全方位的分析可能发生的所有情况!!!
#include<iostream> #include<cstring> #include <string> #include<algorithm> #include<map> #include<stack> #define lson rt<<1 #define rson rt<<1|1 using namespace std; typedef long long ll; const int maxn=2e5+20; int tree[maxn*4]; void insert(int x,int p,int l,int r,int rt) if(l==x&&r==x) tree[rt]=p; return; int mid=(l+r)/2; if(x<=mid) insert(x,p, l, mid, lson); else insert(x,p, mid+1, r, rson); tree[rt]=max(tree[lson],tree[rson]); int qu(int L,int R,int l,int r,int rt) if(L<=l&&r<=R) return tree[rt]; else int mid=(l+r)/2; int ans=0; if(L<=mid) ans=max(ans,qu(L, R, l, mid, lson)); if(R>mid) ans=max(ans,qu(L, R, mid+1, r, rson)); return ans; int ans[maxn],ans_pre[maxn],a[maxn],h[maxn],n,m; map<int ,int> M; int main() int T; cin>>T; while(T--) scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&h[i]); for(int i=0;i<=4*maxn;i++) tree[i]=0; for(int i=1;i<=n;i++) insert(i, h[i], 1, n, 1); stack<int> S; for(int i=n;i>0;i--) while(1) if(S.empty()||S.top()>h[i]) S.push(h[i]); ans[i]=S.size(); break; else S.pop(); a[1]=h[1]; ans_pre[1]=1; for(int i=2,j=h[1];i<=n;i++) if(h[i]>j) ans_pre[i]=ans_pre[i-1]+1; j=h[i]; a[i]=h[i]; else a[i]=a[i-1]; ans_pre[i]=ans_pre[i-1]; for(int i=0;i<m;i++) int p,q; scanf("%d%d",&p,&q); int ans1=ans_pre[p]; if(a[p]<q) if(p!=1&&a[p]==a[p-1]) ans1++; int l=p+1,r=n; if(l<=r&&qu(p+1, n, 1, n, 1)>q) while(r>l+1) int mid=(l+r)/2; if(qu(l, mid, 1, n, 1)>q) r=mid; else l=mid; if(h[l]<=q) l=r; ans1+=ans[l]; else if(p==1) int l=p+1,r=n; if(l<=r&&qu(p+1, n, 1, n, 1)>q) while(r>l+1) int mid=(l+r)/2; if(qu(l, mid, 1, n, 1)>q) r=mid; else l=mid; if(h[l]<=q) l=r; ans1+=ans[l]; else if(a[p]!=a[p-1]) if(q<=a[p-1]) ans1--; q=a[p-1]; int l=p+1,r=n; if(l<=r&&qu(p+1, n, 1, n, 1)>q) while(r>l+1) int mid=(l+r)/2; if(qu(l, mid, 1, n, 1)>q) r=mid; else l=mid; if(h[l]<=q) l=r; ans1+=ans[l]; else ans1=ans_pre[n]; printf("%d\n",ans1);
以上是关于hdu-6406 线段树+二分的主要内容,如果未能解决你的问题,请参考以下文章
HDU 5649 DZY Loves Sorting(二分答案+线段树线段树合并+线段树分割)