Description

国际信息学奥林匹克竞赛将要在日本召开了。为了欢迎全世界的选手们,委员会决定将从机场到宿舍沿路的大楼装饰起来。根据某著名设计师的设计,做装饰的大楼从机场到宿舍的方向必须高度严格递增。也就是说,如果做装饰的大楼从机场开始高度顺次为h1,h2,h3,…,那么必须满足h1

Solution

看完题之后表示没有看懂题目…….看了十分钟,表示日本的题意都埋有玄机,23333终于看懂了。
题意就是有一个经过最长上升子序列DP后的数组,然后随意的删去其中的一个值,让你求出还原之后的合法序列总数。
因为是DP之后的数组,我们先要知道是怎么DP,不然怎么做题?

f[i]=max(f[j]+1,f[i])满足(a[i]>a[j])


因为要求最大值,我们设t表示i之前的f[i]的最大值。


我们可以发现如果当前的f[i]合法,那么前面必须满足t+1>=f[i],那么每次的答案就加上t+1就好了,因为当前这个i的后面可以填充的值域范围是(1…t+1),很明显。


但是还有一种情况,如果t+2=f[i],那么我在这个位置插入一个可以转移到f[i]的数就好了,但是如果这种情况出现了一次以上,那么就不合法了,因为无法在他之前插入一个造成多出两个转移。如果t+2>f[i],那么也不合法。


如果在一个数的左边插入这个数的值,和右边插入这个数的值,造成两次插入是一样的,这样是不行的,所以答案减去ans-1就好了。


注意,如果出现了t+2=f[i]的情况,答案还要重新求一遍,必须要满足能转移过去导致序列合法。


要开long long啊!

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1000007;
int i,j,k,l,t,n,m,a[maxn];
long long ans;
bool bz;
int main(){
freopen("building.in","r",stdin);
freopen("building.out","w",stdout);
scanf("%d",&n);
fo(i,1,n-1){
scanf("%d",&a[i]);
}
fo(i,1,n-1){
if(t==a[i]-1){
t=a[i];
}
else if(t==a[i]-2){
if(bz){
printf("0\n");
return 0;
}
bz=1;
l=a[i];
t=a[i];
}
else if(t<a[i]-2){
printf("0\n");
return 0;
}
ans+=t+1;
}
ans-=n-1;
if(bz){
ans=0;
t=0;
if(t+2==l)ans++;
fo(i,1,n){
t=max(t,a[i]);
if(t+2==l)ans++;
}
printf("%lld\n",ans);
}
else{
printf("%lld\n",ans+1);
}
}