一道分治题
题目大意:
有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;
}