牛客挑战赛36 - 纸飞机

题目链接:牛客挑战赛36 - 纸飞机


题目描述
直线上有n座山峰,第i座的高度为hi。从某座山峰上放飞一架纸飞机,它可以从左往右依次经过一系列高度严格递减的山头。
假设五座山峰的高度依次是3,4,3,2,1。从第一座山峰上放飞的纸飞机可以依次经过第一、四、五座山峰,但不能经过第二、三座山峰。
对于每座山峰,求出要经过除这座山峰外的每座山峰,至少需要放飞多少纸飞机。(每架纸飞机的起点可以不同)
输入描述:
第一行包括一个正整数n。

第二行包括n个正整数,第i个数表示第i座山峰的高度hi。

输出描述:
输出一行,包括n个用空格隔开的正整数,第i个数表示除去第i座山峰的答案。
示例1
输入
复制
5
2 4 3 1 5
输出
复制
2 3 3 3 2


题目就是要放飞机使得每座山峰都有飞机经过。每座山峰是可以经过多次的,所以不难发现这是一个最小链覆盖的问题。

然后:最小链覆盖 = 最长反链

所以我们就是求不包括 i 的最长反链,在这里最长反链就是 非严格上升LIS ,我们枚举每个点,看这个点是否往后面走可以到最长反链即可知道这个点是否可能为答案。

最后看是否有一个以 i 结尾的并且有效的LIS的值是独一无二的,那么这个点就是必然存在LIS当中的。


AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,a[N],d[N],f[N],vis[N],res,st[N],cnt[N]; vector<int> v,g[N];
inline void add(int x,int v){for(;x<=m;x+=(x&(-x))) d[x]=max(d[x],v);}
inline int ask(int x){int s=0; for(;x;x-=x&(-x)) s=max(s,d[x]); return s;}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]),v.push_back(a[i]);
    sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());
    m=v.size(); vis[0]=1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
    for(int i=1;i<=n;i++){
        f[i]=ask(a[i])+1;   add(a[i],f[i]);     res=max(res,f[i]);
    }
    vis[res+1]=1e9;
    for(int i=n;i>=1;i--)   if(vis[f[i]+1]>=a[i]){
        vis[f[i]]=max(a[i],vis[f[i]]);  st[i]=1;
    }
    for(int i=1;i<=n;i++)   if(st[i])   cnt[f[i]]++;
    for(int i=1;i<=n;i++)   printf("%d ",res-(st[i]&&cnt[f[i]]==1));
    return 0;
}
发布了443 篇原创文章 · 获赞 238 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43826249/article/details/104028715