题解 P5899 【[COCI 2015]Norma】
【题解】Norma [COCI2014] [SP22343]
传送门:
【题目描述】
给定一个整数
【分析】
询问过于奇葩,万能的线段树都没法搞,单调队列也许可做,但太复杂了。
可以用类似
对于一个区间
考虑处理一个区间
对于每个
用
设两个指针
可知这一整段的元素数值范围都在
第
这部分比较难想,在草稿纸上比划了好久才搞出来,而且还不太好描述。
分为
以
由于子序列长度在不断的变化,但
这个东西不太好描述,见下面的式子:
该做法的正确性来自于:随着右端点的递增,
其实只要
第
最后,注意取膜!!!开
时间复杂度为:
【Code】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define LL long long
#define Re register LL
using namespace std;
const LL N=5e5+3,logN=19,P=1e9;
LL n,ans,a[N],S1[N],S2[N],S3[N],S1_[N],S2_[N],S3_[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void sakura(Re L,Re R){
if(L==R){(ans+=a[L]*a[L]%P)%=P;return;}//这个特判必须要加
if(L+1==R){(ans+=(a[L]*a[L]%P+a[R]*a[R]%P+a[L]*a[R]%P*2%P)%P)%=P;return;}//这里好像不用特判也可以QAQ
Re mid=L+R>>1,mi=a[mid],mx=a[mid],i=mid,j=mid,k=mid;//注意j,k预处理为mid而不是mid+1
Re MI=a[mid+1],MX=a[mid+1];//这里MI,MX和上面的mi,mx取inf,-inf也可以
S1[mid]=S2[mid]=S3[mid]=S1_[mid]=S2_[mid]=S3_[mid]=0;//重置前缀和
for(Re i=mid+1;i<=R;++i){
MI=min(MI,a[i]),MX=max(MX,a[i]);//更新前缀最大值
(S1[i]=S1[i-1]+MI*(i-(mid+1)+1)%P)%=P,(S1_[i]=S1_[i-1]+MI)%=P;//递推更新S1
(S2[i]=S2[i-1]+MX*(i-(mid+1)+1)%P)%=P,(S2_[i]=S2_[i-1]+MX)%=P;//递推更新S2
(S3[i]=S3[i-1]+MI*MX%P*(i-(mid+1)+1)%P)%=P,(S3_[i]=S3_[i-1]+MI*MX%P)%=P;//递推更新S3
}
while(i>=L){
mi=min(mi,a[i]),mx=max(mx,a[i]);
while(j<R&&a[j+1]>mi)++j;//移动MI指针j
while(k<R&&a[k+1]<mx)++k;//移动MX指针k
Re w1=min(j,k),w2=max(j,k);//获取三部分的两个分界点
if(w1>mid)(ans+=mi*mx%P*((mid+1-i+1+w1-i+1)*(w1-(mid+1)+1)/2%P)%P)%=P;//完全满足的部分
if(j>w1)//满足mi但不满足mx
(ans+=mi*((S2[j]-S2[w1]+P)%P+(mid-i+1)*(S2_[j]-S2_[w1]+P)%P)%P)%=P;
if(k>w1)//满足mx但不满足mi
(ans+=mx*((S1[k]-S1[w1]+P)%P+(mid-i+1)*(S1_[k]-S1_[w1]+P)%P)%P)%=P;
(ans+=((S3[R]-S3[w2]+P)%P+(mid-i+1)*(S3_[R]-S3_[w2]+P)%P)%P)%=P;//完全不满足的部分
--i;//移动左指针
}
sakura(L,mid),sakura(mid+1,R);//递归搞下面
}
int main(){
// freopen("norma.in","r",stdin);
// freopen("norma.out","w",stdout);
in(n);
for(Re i=1;i<=n;++i)in(a[i]);
sakura(1,n);
printf("%lld\n",ans%P);
// fclose(stdin);
// fclose(stdout);
return 0;
}