JZOJ 5611. 【NOI2018模拟3.29】第2题

题目描述

这里写图片描述

解题思路

这题真TM恶心。
但是,我们必须在杂乱无章的max序列中找到一些有用的信息。
如果求T(A)T(A),那么直接用个单调栈或者分治一波搞定。
但是要求T(T(A))T(T(A))的值,这使我们不得不寻找这个序列的某些规律。

整体感知

解题的方法一定是扫一遍或者对于长度为nn的区间分治一波。
不能将T(A)构造出来,而且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[n1..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块,第i块的长度为ni+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在第i个区间,那么rr在第i+2个区间,这些元素都是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])出现了(rl+1)(r−l+1)次。
考虑暴力地枚举数对(l,r)(l,r),如果确定了(l,r)(l,r),那么会对答案贡献
(rl+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,设ml=max(A[l]...A[mid]),则用rr指针维护
mr=max(A[mid+1]...A[r])=mlrr的最大值。
则这次l的左移,(l,r),mid+1<=r<=r(l,r′),mid+1<=r′<=r对答案的贡献为:
Ans+=Σrj=mid+1ml(jl+1)Ans+=Σj=mid+1rml∗(j−l+1)
将式子拆开,得Ans+=ml(C2rl+1C2midl+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]))(jl+1)Ans+=Σj=mid+1r(max(a[mid+1]...a[j]))∗(j−l+1)
所以,ll的某次左移对答案的贡献为:

Ans+=ml(Crl+12Cmidl+12)+qz2[r+1]+qz1[r+1](1i)

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,设ml=max(A[l]...A[mid]),则用rr指针维护
mr=max(A[mid+1]...A[r])=mlrr的最大值。

考虑这个跨块的区间的最大值是什么。

假设这个区间为[l,r],则对答案的贡献为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[]的右边找到一个位置p,使得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])

Ans+=max(ml,mr)min(d[l],d[r])Ans+=max(ml,mr)∗min(d[l],d[r])
.
相当于对于每一个ll,要在O(1)的时间复杂度内算出
Σ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,r1]r∈[1,r−1]max(ml,mr)=mlmax(ml,mr)=mlmin(d[l],d[r])=d[r]min(d[l],d[r])=d[r]
r[r,p1]r∈[r,p−1]max(ml,mr)=mrmax(ml,mr)=mrmin(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)=mrmin(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)=mlmin(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)=mrmin(d[l],d[r])=d[l]min(d[l],d[r])=d[l]
r>pr>p时,
r[1,p1]r∈[1,p−1]max(ml,mr)=mlmax(ml,mr)=mlmin(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)=mlmin(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)=mrmin(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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值