CodeChef-SAFPAR
不会高妙做法。 😦
首先拆条件:
-
\(\min \le j-i+1\)。
-
\(j-i+1\le \max\)。
第一个条件发现越长,越容易满足,是有单调性的。因此可以直接维护能达到的区间(使用 ST 表)。
第二个条件,考虑 cdq 分治优化 dp。假设已经求出了 \(l\sim mid\) 要更新 \(mid+1,r\),注意是 \(i=l+1\sim mid+1\) 到 \(j\)。
有两种情况:
-
\(\max\) 在左边。
-
\(\max\) 在右边。
第一种是一个点更新一个区间,第二种是一个点被一个区间贡献。为了维护,还要记录一个值控制的区间,也就是是那个区间的最大值。一个差分一个前缀和维护。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e5+5;
const ll mod = 1e9+7;
int n,a[N],st[N][20],lg[N],lf[N],rt[N],stk[N],top,mx[N],mn[N];
ll dp[N],cf[N],sm[N];
int qy(int l,int r){
int t=lg[r-l+1];
return min(st[l][t],st[r-(1<<t)+1][t]);
}
void sol(int l,int r){
if (l==r){
return;
}
int mid=l+r>>1;
sol(l,mid);
for (int i=l,j=mid+1; i<=mid; i++){
while (j<=r && qy(i+1,j)>j-i) j++;
mx[i]=j;
}
for (int j=r,i=mid; j>mid; j--){
while (i>=l && qy(i+1,j)>j-i) i--;
mn[j]=i;
}
for (int j=mid; j<=r; j++) cf[j]=0;
int Mx=0,ps=0;
for (int i=mid; i>=l; i--){
if (a[i+1]>Mx) Mx=a[i+1],ps=i;
int rb=rt[ps+1]-1;
// j-i<=Mx
rb=min(rb,Mx+i); rb=min(rb,r);
if (mx[i]<=rb) cf[mx[i]]+=dp[i],cf[rb+1]-=dp[i];
}
for (int j=mid+1; j<=r; j++){
cf[j]+=cf[j-1],dp[j]+=cf[j];
cf[j]%=mod,dp[j]%=mod;
}
sm[l-1]=0,sm[l]=dp[l];
for (int i=l+1; i<=mid; i++) sm[i]=(sm[i-1]+dp[i]),sm[i]%=mod;
Mx=ps=0;
for (int j=mid+1; j<=r; j++){
if (a[j]>Mx) Mx=a[j],ps=j;
if (ps==mid+1) continue;
int lb=lf[ps];
lb=max(lb,j-Mx); lb=max(lb,l);
if (lb<=mn[j]) dp[j]=(dp[j]+sm[mn[j]]-sm[lb-1]+mod)%mod;
}
sol(mid+1,r);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for (int i=1; i<=n; i++) cin>>a[i],st[i][0]=a[i];
for (int i=2; i<N; i++) lg[i]=lg[i/2]+1;
for (int i=1; i<20; i++){
for (int j=1; j+(1<<i)-1<=n; j++){
st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
}
}
stk[++top]=0;
for (int i=1; i<=n; i++){
while (top>1 && a[stk[top]]<a[i]) top--;
lf[i]=stk[top],stk[++top]=i;
}
stk[top=1]=n+1;
for (int i=n; i; i--){
while (top>1 && a[stk[top]]<=a[i]) top--;
rt[i]=stk[top],stk[++top]=i;
}
dp[0]=1;
sol(0,n);
cout<<(dp[n]%mod+mod)%mod<<"\n";
return 0;
}
浙公网安备 33010602011771号