【NOIP模拟】装饰大楼

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
#include
#include
#include
#include
#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(t2){
            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);
    }
}

你可能感兴趣的:(noip,DP,JOI)