题目描述
解题思路
这题真TM恶心。
但是,我们必须在杂乱无章的max序列中找到一些有用的信息。
如果求T(A)T(A),那么直接用个单调栈或者分治一波搞定。
但是要求T(T(A))T(T(A))的值,这使我们不得不寻找这个序列的某些规律。
整体感知
解题的方法一定是扫一遍或者对于长度为nn的区间分治一波。
不能将构造出来,而且T(A)T(A)的某些特征一定是突破口。
由于要求T(T(A))T(T(A))的值,不需要求T(T(A))T(T(A))的具体数列,所以找到T(A)T(A)的某些特征即可。(这个我觉得和ZJOI2018的Day1T1有相似性。”k=9k=9只用算7次”。)
寻找线索
回到题目中。可以清楚地发现,
T(A)=T[1],T[2],T[3],...,T[n],T[1..2],T[2..3],...,T[n−1..n],...,...,T[1..n]T(A)=T[1],T[2],T[3],...,T[n],T[1..2],T[2..3],...,T[n−1..n],...,...,T[1..n]
①将T(A)T(A)分成nn块,第块的长度为n−i+1n−i+1。
②很容易知道a[i]a[i]中的最大值MXMX,如果在T(A)T(A)中查询某段区间[l,r][l,r],它严格地跨过了一个完整的区间(比如说l在第一个区间,r在第三个区间,严格地跨过了第二个区间)
③如果l和r同处一个块内,那么max(T(A)[l..r]=max(T[l]...T[r])max(T(A)[l..r]=max(T[l]...T[r])。
④还有l和r在相邻的块内。这个很恶心。
具体做法
先搞严格跨块的。假设ll在第个区间,那么rr在第个区间,这些元素都是MXMX。均摊O(1)O(1)算就好了。
然后对于l,rl,r严格在块内的,会有两个难点。①T(A)T(A)需要脑补。②T(A)T(A)有很多个块,若确定了l,rl,r,还可能要算不止一次答案。
重新寻找线索。请看线索③。这给我们一条思路。如果确定了l,rl,r,那么可以确定max(A[l]...A[r])max(A[l]...A[r])出现了(r−l+1)(r−l+1)次。
考虑暴力地枚举数对(l,r)(l,r),如果确定了(l,r)(l,r),那么会对答案贡献
(r−l+1)∗max(A[l]...A[r])(r−l+1)∗max(A[l]...A[r])。
可以发现,或许会有很多max(A[l]...A[r])max(A[l]...A[r])是相等的。
所以要对max(A[l]...A[r])max(A[l]...A[r])有一个明确的整理方案,这个方案能够将相同的max(A[l]...A[r])max(A[l]...A[r])合并,且枚举的数对(l,r)(l,r)尽量少。
可以采用分值的方法,假设当前分治的区域为(L,R)(L,R),中点为midmid,则查找穿过midmid的区间(l,r)(l,r)。这样子可以使所有的(l,r)(l,r)不重不漏。
在思考这类问题的时候,注意两个东西。①(l,r)(l,r)及它的区间的长度②这个区间的元素的最大值。
这道题有个棘手的地方,因为最大值要考虑l..midl..mid的和mid+1..rmid+1..r的。
思路来到这里,容易实现的一种方案是:(用这个方案的原因:mlml会单调不递减)
手动左移ll,设,则用rr指针维护
的rr的最大值。
则这次的左移,(l,r′),mid+1<=r′<=r(l,r′),mid+1<=r′<=r对答案的贡献为:
Ans+=Σrj=mid+1ml∗(j−l+1)Ans+=Σj=mid+1rml∗(j−l+1)。
将式子拆开,得Ans+=ml∗(C2r−l+1−C2mid−l+1)Ans+=ml∗(Cr−l+12−Cmid−l+12)。
当然,不要忽略数对(l,r′),r′>r(l,r′),r′>r。这些数对对答案的贡献为:
Ans+=Σrj=mid+1(max(a[mid+1]...a[j]))∗(j−l+1)Ans+=Σj=mid+1r(max(a[mid+1]...a[j]))∗(j−l+1)。
所以,ll的某次左移对答案的贡献为:
设mx[i]=Σj>=imax(a[mid+1]...a[j])mx[i]=Σj>=imax(a[mid+1]...a[j])。
qz1,qz2qz1,qz2为两个维护后缀和的数组,
qz1[i]=Σj>=imx[j]qz1[i]=Σj>=imx[j],qz2[i]=Σj>=imx[j]∗jqz2[i]=Σj>=imx[j]∗j。
最恶心的来了。
如何求l和r在相邻的块内的?
参照求块内的,手动左移ll,设,则用rr指针维护
的rr的最大值。
考虑这个跨块的区间的最大值是什么。
假设这个区间为,则对答案的贡献为max(max(a[l]...a[n]),max(a[1]...a[l]))max(max(a[l]...a[n]),max(a[1]...a[l])).
为了减少枚举的次数,考虑[l,r][l,r]可以出现几次。来看下面的例子。
b[]=1 2 3 4 5 1 2 3 4 5
d[]=5 4 3 2 1 0 1 2 3 4
画图可知,[l,r][l,r]出现了min(d[左边的l],d[右边的r])min(d[左边的l],d[右边的r])次。
手动枚举ll时,可以在b[]的右边找到一个位置,使得d[l]=d[p]d[l]=d[p]。为什么要这么做?
考虑怎么计算对答案的贡献。
显然,一个数对(l,r)(l,r)对答案的贡献。
设ml=max(a[l]...a[n]),mr=max(a[1]...a[r])ml=max(a[l]...a[n]),mr=max(a[1]...a[r])
相当于对于每一个ll,要在的时间复杂度内算出
Σnr=1max(ml,mr)∗min(d[l],d[r])Σr=1nmax(ml,mr)∗min(d[l],d[r])
考虑哪些max(ml,mr)max(ml,mr)是相同的,哪些min(d[l],d[r])min(d[l],d[r])是相同的。
当r<pr<p时,
r∈[1,r−1]r∈[1,r−1],max(ml,mr)=mlmax(ml,mr)=ml,min(d[l],d[r])=d[r]min(d[l],d[r])=d[r]。
r∈[r,p−1]r∈[r,p−1],max(ml,mr)=mrmax(ml,mr)=mr,min(d[l],d[r])=d[r]min(d[l],d[r])=d[r]。
r∈[p,n]r∈[p,n],max(ml,mr)=mrmax(ml,mr)=mr,min(d[l],d[r])=d[l]min(d[l],d[r])=d[l]。
当r=pr=p时,
r∈[1,p]r∈[1,p],max(ml,mr)=mlmax(ml,mr)=ml,min(d[l],d[r])=d[r]min(d[l],d[r])=d[r]。
r∈[p+1,n]r∈[p+1,n],max(ml,mr)=mrmax(ml,mr)=mr,min(d[l],d[r])=d[l]min(d[l],d[r])=d[l]。
当r>pr>p时,
r∈[1,p−1]r∈[1,p−1],max(ml,mr)=mlmax(ml,mr)=ml,min(d[l],d[r])=d[r]min(d[l],d[r])=d[r]。
r∈[p,r]r∈[p,r],max(ml,mr)=mlmax(ml,mr)=ml,min(d[l],d[r])=d[l]min(d[l],d[r])=d[l]。
r∈[r+1,n]r∈[r+1,n],max(ml,mr)=mrmax(ml,mr)=mr,min(d[l],d[r])=d[l]min(d[l],d[r])=d[l]。
剩下的自己慢慢算。考虑维护什么前缀和后缀和之类的。
详细的结果请见我的标。太恶心了。
有的时候虽然理论上某些式子是成立的,但是在实际操作中会有问题。//比如处理界点
心得
顺便一提,LL有毒!
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 200010
#define mo 1000000007
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
LL i,j,k,l,r,n,m,ans,temp;
LL qz1[N],qz2[N];
LL x,y,z,p,ny;
LL a[N],MX;
LL mx[N];
LL calc(LL n){return (n*(n+1)%mo*ny)%mo;}
LL Max(LL x,LL y){return x>y?x:y;}
void dg(LL l,LL r){
if(l==r){
ans=(ans+a[l])%mo;
return;
}
LL mid=(l+r)>>1;
mx[mid]=a[mid];fd(i,mid-1,l)mx[i]=Max(mx[i+1],a[i]);
mx[mid+1]=a[mid+1];fo(i,mid+2,r)mx[i]=Max(mx[i-1],a[i]);
qz1[r+1]=qz2[r+1]=0;
fd(i,r,mid+1){
qz1[i]=(qz1[i+1]+mx[i])%mo;
qz2[i]=(qz2[i+1]+i*mx[i]%mo)%mo;
}
LL i,j=mid;
fd(i,mid,l){
for(;j<r&&mx[j+1]<=mx[i];j++);
ans=(ans+(calc(j-i+1)-calc(mid-i+1)+mo)%mo*mx[i]%mo)%mo;
ans=(ans+(qz2[j+1]+qz1[j+1]*(1-i+mo)%mo)%mo)%mo;
}
dg(l,mid);
dg(mid+1,r);
}
int main(){
freopen("trans.in","r",stdin);
freopen("trans.out","w",stdout);
ny=500000004;
scanf("%lld",&n);
fo(i,1,n)scanf("%lld",&a[i]),MX=Max(MX,a[i]);
temp=n;l=n;
fo(i,2,n){
r=calc(n)-(n-i+1)-temp;
ans=(ans+(l*r%mo*MX)%mo)%mo;
temp+=n-i+1;
l--;
}
dg(1,n);
fo(i,1,n){
mx[i]=Max(mx[i-1],a[i]);
qz1[i]=(qz1[i-1]+mx[i]*(i-1))%mo;
}
fd(i,n,1)qz2[i]=(qz2[i+1]+mx[i])%mo;
r=0;x=0;y=1;
fd(l,n,1){
x=Max(x,a[l]);
for(;r<n&&mx[r+1]<=x;r++);
p=y+1;
if(r<p)ans=(ans+x*calc(r-1)%mo+(qz1[p-1]-qz1[r]+mo)%mo+qz2[p]*y%mo)%mo;else
if(r==p)ans=(ans+calc(r-1)*x%mo+y*qz2[p+1]%mo)%mo;else
ans=(ans+x*calc(p-1)+(r-p)*x*y%mo+y*qz2[r+1]%mo)%mo;
y++;
}
printf("%lld",ans);
return 0;
}