Codechef August Challenge 2018 : Safe Partition

时间:2023-03-09 02:06:11
Codechef August Challenge 2018 : Safe Partition

传送门

(虽然是A了但是不知道复杂度是不是正确的

考虑以某个位置为结尾的合法划分

先考虑min,带来的影响是限制了最小长度,预处理出这个最小长度后,这可以在处理到这个数时,把不能算的部分去掉(不满足min条件的话必定满足max条件)。

单独考虑max条件

对于每一个数,找出以它为最大值的区间后,暂时不考虑min,就只限制了划分的最大长度,这时分类讨论一下(见代码),有一部分需要暴力处理(不会证复杂度)

#include<cstdio>
#include<algorithm>
#define MN 510000
using namespace std; const int MOD=1e9+;
int n,m,a[MN],_l[MN],st[MN],top=,v[MN],V[MN],tmp,ans,fi[MN],ne[MN];
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline void M(int &x){while(x>=MOD)x-=MOD;while(x<)x+=MOD;}
inline int ask(int l,int r){int a=V[r-]-(l==?:V[l-]);M(a);return a;}
inline void add(int l,int r,int sum){M(v[l]+=sum);M(v[r+]-=sum);}
inline void work(int s,int _s,int l){
int sum=;
for (int i=;i<=l;i++){
add(_s,_s+i-,ask(s+i-,s+i-));
}
}
int main(){
scanf("%d",&n);
for (int i=;i<=n;i++) scanf("%d",&a[i]); int L=,R=;
for (int i=;i<=n;i++){
while(L<=R&&a[st[R]]>=a[i]) R--;
st[++R]=i;
while(L<R&&i-a[st[L+]]+>=st[L]) L++;
_l[i]=max(min(st[L],i-a[st[L]]+),);
} V[]=;
for (int i=;i<=n;i++){
while(a[st[top]]<=a[i]&&top) ne[st[top]]=i,top--;fi[i]=st[top];
st[++top]=i;
}
for (int i=;i<=n;i++){
int l=fi[i]+,r=ne[i]?ne[i]-:n,sum;
if (a[i]>=r-l+){
add(i,r,ask(l,i));
}else if (a[i]>=i-l+&&a[i]>=r-i+){
add(i,l+a[i]-,ask(l,i));
add(l+a[i],r,ask(r-a[i]+,i));
work(l+,l+a[i],r-(l+a[i]));
}else if (a[i]<=i-l+&&a[i]<=r-i+){
work(i-a[i]+,i,a[i]);
}else if (a[i]<=i-l+){
add(i,r,ask(r-a[i]+,i));
work(i-a[i]+,i,r-i);
}else if (a[i]<=r-i+){
add(i,l+a[i]-,ask(l,i));
work(l+,l+a[i],i-l);
}
M(tmp+=v[i]);
M(ans=tmp-ask(_l[i]+,i));
M(V[i]=ans+V[i-]);
}
printf("%d\n",ans);
}