首页 > 代码库 > XTU 1205 Range

XTU 1205 Range

还是五月湘潭赛的题目,当时就是因为我坑。。。连个银牌都没拿到,擦。

这个题目枚举区间是不可能的,明显是要考虑每个数对全局的影响,即找到每个数最左和最右能满足是最大的位置 以及 最小的时候,相乘即为该数字影响的区间总数。当时想到的是用线段树,建树的时候求出最大和最小值,然后在每个数往里面搜索,比赛的时候敲挫了,那个时候真的线段树写的很挫,而且没考虑过一个问题,就是相同的时候怎么办,按刚刚的算法,会算重复的,所以一个好的方法是如果有相同的,往左搜的时候搜到等于该数值的时候停止,往右搜的时候搜到大于该数值的时候停止(求最大的时候),求最小的时候也是一样的处理

然后搜左边的时候优先走右子树,右子树没有 再找左子树。。同理右边先搜左孩子,没有再搜右孩子,注意些细节以及可以剪剪枝。

线段树写这个题目其实很费力,有种更好的方法用桟来做,栈在求最大最小的时候往往很给力,像这个题目,我比如在求左边最大的时候,我当前数,如果碰到栈顶数比它大于等于的,就直接停止了,入桟,否则就一直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 __int64using 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\n",++kase,sum);    }    return 0;}

 

XTU 1205 Range