还是五月湘潭赛的题目,当时就是因为我坑。。。连个银牌都没拿到,擦。
这个题目枚举区间是不可能的,明显是要考虑每个数对全局的影响,即找到每个数最左和最右能满足是最大的位置 以及 最小的时候,相乘即为该数字影响的区间总数。当时想到的是用线段树,建树的时候求出最大和最小值,然后在每个数往里面搜索,比赛的时候敲挫了,那个时候真的线段树写的很挫,而且没考虑过一个问题,就是相同的时候怎么办,按刚刚的算法,会算重复的,所以一个好的方法是如果有相同的,往左搜的时候搜到等于该数值的时候停止,往右搜的时候搜到大于该数值的时候停止(求最大的时候),求最小的时候也是一样的处理
然后搜左边的时候优先走右子树,右子树没有 再找左子树。。同理右边先搜左孩子,没有再搜右孩子,注意些细节以及可以剪剪枝。
线段树写这个题目其实很费力,有种更好的方法用桟来做,栈在求最大最小的时候往往很给力,像这个题目,我比如在求左边最大的时候,我当前数,如果碰到栈顶数比它大于等于的,就直接停止了,入桟,否则就一直pop下去,直到遇到比它大于等于的或者桟空,这个时候得到了左边区间最大,并且把当前值放进去,因为我只保留左边区间离下个数最近的最大的数即可,如果连这个数都不能阻挡下一个数,那之前被pop掉的肯定也阻挡不了,所以扫一遍即可
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define lson rt<<1,l,mid #define rson rt<<1|1,mid+1,r #define LL __int64 using namespace std; const int N = 100010; int dmin[N<<2],dmax[N<<2]; int A[N]; int n; void build(int rt,int l,int r) { if (l>=r){ dmin[rt]=A[l]; dmax[rt]=A[l]; return; } int mid=(l+r)>>1; build(lson); build(rson); dmax[rt]=max(dmax[rt<<1],dmax[rt<<1|1]); dmin[rt]=min(dmin[rt<<1],dmin[rt<<1|1]); } int query1(int L,int R,int val,int rt,int l,int r) { if (L>R) return R; if (l>=r){ if(l>=L &&l<=R && A[l]>=val){ return l; } else return 0; } int mid=(l+r)>>1; if (R<=mid){ if (dmax[rt<<1]>=val) return query1(L,R,val,lson); else return 0; } int ret=0; if (dmax[rt<<1|1]>=val){ ret=query1(L,R,val,rson); } if (ret>0) return ret; if (dmax[rt<<1]>=val) return query1(L,R,val,lson); else return 0; } int query2(int L,int R,int val,int rt,int l,int r) { if (L>R) return L; if (l>=r){ if (l>=L && l<=R && A[l]>=val){ return l; } else return n+1; } int mid=(l+r)>>1; if (L>mid){ if (dmax[rt<<1|1]>=val) return query2(L,R,val,rson); else return n+1; } int ret=n+1; if (dmax[rt<<1]>=val){ ret=query2(L,R,val,lson); } if (ret<n+1) return ret; if (dmax[rt<<1|1]>=val) return query2(L,R,val,rson); else return n+1; } int query3(int L,int R,int val,int rt,int l,int r) { if (L>R) return R; if (l>=r){ if (L<=l && l<=R && A[l]<=val){ return l; } else return 0; } int mid=(l+r)>>1; if (R<=mid){ if (dmin[rt<<1]<=val){ return query3(L,R,val,lson); } else return 0; } int ret=0; if (dmin[rt<<1|1]<=val) ret=query3(L,R,val,rson); if (ret>0) return ret; if (dmin[rt<<1]<=val) return query3(L,R,val,lson); else return 0; } int query4(int L,int R,int val,int rt,int l,int r) { if (L>R) return L; if (l>=r){ if (L<=l && r<=R && A[l]<=val){ return l; } else return n+1; } int mid=(l+r)>>1; if (L>mid){ if (dmin[rt<<1|1]<=val){ return query4(L,R,val,rson); } else return n+1; } int ret=n+1; if (dmin[rt<<1]<=val){ ret=query4(L,R,val,lson); } if (ret<n+1) return ret; else if (dmin[rt<<1|1]<=val) return query4(L,R,val,rson); return n+1; } int main() { int t; int kase=0; scanf("%d",&t); while (t--) { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&A[i]); build(1,1,n); LL sum=0; for (int i=1;i<=n;i++){ int l1=query1(1,i-1,A[i],1,1,n); int l2=query2(i+1,n,A[i]+1,1,1,n); int l3=query3(1,i-1,A[i],1,1,n); int l4=query4(i+1,n,A[i]-1,1,1,n); //cout<<"Test "<<i<<endl; // cout<<l1<<" "<<l2<<" "<<l3<<" "<<l4<<endl; sum+=(LL)(i-l1)*(LL)(l2-i)*(LL)A[i]; sum-=(LL)(i-l3)*(LL)(l4-i)*(LL)A[i]; } sum+=(LL)n*(n+1)/2; printf("Case %d: %I64d ",++kase,sum); } return 0; }