一道分治题

一道分治题

题目大意:

有n个数分别为a1,a2,……,an,
for(i=1;i<=n;i++)
for(j=i;j<=n;j++)
ans+=(j-i+1)*min(i~j)*max(i~j)
求ans,n<=500000

solution:

T2考虑分治,首先想到的做法是每次找最大值,以最大值为中心分成两段,时间复杂度Ω(n lgn)。可是一个有序的数列会把它卡成n²,所以一定要找中间值把区间断开。令mid=(l+r)/2,对于i∈[l,mid],求出i~mid的后缀Min[i]、Max[i],对于i∈[mid+1,r],求出mid+1~i的前缀Min[i],Max[i]。由于使用分治,所以只要考虑左端点i∈[I,mid],右端点j∈[mid+1,r]的区间对答案的贡献。要考虑4种情况,就是最大值在左边或右边,最小值在左边还是右边。为了解决数字大小相同的问题,在大小相同时,默认靠前的值比较小。
1、 Min[i]<=Min[j]且Max[i]>=Max[j]从大到小枚举i,随着i的减小,j的合法区域一定是一段区间[L,R],且L=mid+1,R单调递增对答案的贡献为这里写图片描述这里写图片描述维护这一个区间就可以了
2、 Min[i]>Min[j]且Max[i]<Max[j]
与第一种情况类似,但是L不一定是mid+1,R=r对答案的贡献为 这里写图片描述 =这里写图片描述 同理在维护区间时维护这两个和就行了
3、 Max[i]>=Max[j]且Min[i]>Min[j]还是从大到小枚举i,j的合法区域依然是一个区间[L,R],但是L,R都是∈[mid+1,r]的,这时需要维护的是一个类似队列的,不再是栈对答案的贡献是 这里写图片描述 =这里写图片描述 同样的方法也可以维护,就是需要队列的头维护
4、 Max[i]<Max[j]且Min[i]<=Min[j]与3、的方法一模一样,就是Max和Min反一下。解决了这个问题,就可以顺利的分治了,T(n)=2T(n/2)+Θ(n)= Θ(n lg n)

code:

#include<cstdio>
#include<algorithm>
const int p=1000000007;
inline int mo(int x){
    if(x>=p)
        return x-p;
    return x;
}
int n,a[500005],ans,Max[500005],Min[500005],sum,last,sum1,head,tail;
void solve(int l,int r){
    if(l==r){
        ans=mo(ans+(long long)a[l]*(long long)a[l]%p);
        return;
    }
    int mid=(l+r)>>1;
    Max[mid]=Min[mid]=a[mid];
    for(int i=mid-1;i>=l;i--){
        Max[i]=std::max(Max[i+1],a[i]);
        Min[i]=std::min(Min[i+1],a[i]);
    }
    Max[mid+1]=Min[mid+1]=a[mid+1];
    for(int i=mid+2;i<=r;i++){
        Max[i]=std::max(Max[i-1],a[i]);
        Min[i]=std::min(Min[i-1],a[i]);
    }
    last=mid+1;
    sum=0;
    for(int i=mid;i>=l;i--){
        while(last<=r&&Min[last]>=Min[i]&&Max[last]<=Max[i]){
            last++;
            sum=mo(sum+last);
        }
        ans=mo(ans+(long long)Max[i]*(long long)mo(sum-(long long)i*(long long)(last-1-mid)%p+p)%p*Min[i]%p);
    }
    last=r+1;
    sum=sum1=0;
    for(int i=l;i<=mid;i++){
        while(last>mid+1&&Min[last-1]<Min[i]&&Max[last-1]>Max[i]){
            last--;
            sum=mo(sum+(long long)Max[last]*(long long)Min[last]%p*(long long)(last+1)%p);
            sum1=mo(sum1+(long long)Max[last]*(long long)Min[last]%p);
        }
        ans=mo((ans+sum-(long long)sum1*(long long)i%p)%p+p);
    }
    sum=sum1=0;
    head=mid+1;
    tail=mid;
    for(int i=mid;i>=l;i--){
        while(tail<r&&Max[i]>=Max[tail+1]){
            tail++;
            sum=mo(sum+(long long)Min[tail]*(long long)(tail+1)%p);
            sum1=mo(sum1+Min[tail]);
        }
        while(head<=tail&&Min[i]<=Min[head]){
            sum=mo(sum-(long long)Min[head]*(long long)(head+1)%p+p);
            sum1=mo(sum1-Min[head]+p);
            head++;
        }
        ans=mo(ans+(long long)Max[i]*(long long)(sum-(long long)i*(long long)sum1%p+p)%p);
    }
    sum=sum1=0;
    head=mid+1;
    tail=mid;
    for(int i=mid;i>=l;i--){
        while(tail<r&&Min[i]<=Min[tail+1]){
            tail++;
            sum=mo(sum+(long long)Max[tail]*(long long)(tail+1)%p);
            sum1=mo(sum1+Max[tail]);
        }
        while(head<=tail&&Max[i]>=Max[head]){
            sum=mo(sum-(long long)Max[head]*(long long)(head+1)%p+p);
            sum1=mo(sum1-Max[head]+p);
            head++;
        }
        ans=mo(ans+(long long)Min[i]*(long long)(sum-(long long)i*(long long)sum1%p+p)%p);
    }
    solve(l,mid);
    solve(mid+1,r);
}
int main(){
    freopen("dance.in","r",stdin);
    freopen("dance.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    solve(1,n);
    printf("%d\n",ans);
    return 0;
}

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38601996/article/details/78419150
文章标签: 分治 分治算法
个人分类: 分治算法
上一篇51nod1681公共祖先(dfs序+树状数组)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭