牛客挑战赛36 C 纸飞机 最小链覆盖、最长反链学习

题目链接

这题自己瞎搞贪心写了半天,没做出来。

献上题解:

什么是最小链覆盖?

献上百度知识点:

相应的,深度了解最小链覆盖:最小链覆盖

有用的大概就是下面这个:

什么是最长反链呢?

在充分了解了最小链覆盖和最长反链后,为什么两者是等价的呢?

看证明最长反链=最小链覆盖(证明+解析)。这个证明我就没有细细的看了。。

 

在充分了解了最长反链和最小链覆盖后,开始理解这题。。

这题不就是一个无向图(递减),且要 求最小链覆盖。

然后集合反链性质 简单想想就知道了   数字递减构成的图反链==数字不递减构成的图(举几个样例就可以了:3 2 4 1)

然后通过求最长不递减序列值就可以容易得到答案了。

判断某位i,是否 存在 最长序列中 且 是唯一的节点,答案就是 最长序列长度-1 可以存在多个序列中,所以要做唯一判断

倒着扫一遍dp(dp[i]:当前i 能构成的最长不递减长度)判断当前的dp[i] 后面是否存在dp[i]+1的权值mx>=a[i]即可。

维护一个后缀最大值即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N],dp[N],mx[N];//dp[i]:当前i 能构成的最长不递减长度
int vis[N],num[N];//vis判断是否存在某个最长序列中,
int mxval[N];
int X[N],len;//离散化
int n;
int getid(int x)
{
    return lower_bound(X+1,X+1+len,x)-X;
}
/*
权值树状数组
*/
int low(int x)
{
    return x&(-x);
}
void add(int x,int val)
{
    for(;x<=n;x+=low(x)) mx[x]=max(mx[x],val);
}
int qu(int x)
{
    int res=0;
    for(;x;x-=low(x)) res=max(res,mx[x]);
    return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);
        X[i]=a[i];
    }

    sort(X+1,X+1+n);
    len=unique(X+1,X+1+n)-X-1;

    for(int i=1;i<=n;++i){
        a[i]=getid(a[i]);
    }

    int ans=0;
    for(int i=1;i<=n;++i){
        int mxx=qu(a[i]);
        dp[i]=mxx+1;
        ans=max(ans,dp[i]);
        add(a[i],dp[i]);
    }
//    puts("dp");
//    for(int i=1;i<=n;++i) printf("%d ",dp[i]);
//    puts("");
//    printf("ans:%d\n",ans);
//
//    printf("*****\n");

    /*判断某位i,存在 最长序列中且是唯一的话,答案就是 最长序列长度-1
     可以存在多个序列中,所以要做唯一判断
    */
    for(int i=n;i;i--)
    {
        if(dp[i]==ans){
            num[dp[i]]++;
            mxval[dp[i]]=max(mxval[dp[i]],a[i]);
            vis[i]=1;
        }
        else{
            if(mxval[dp[i]+1]>=a[i]){
                num[dp[i]]++;
                vis[i]=1;
                mxval[dp[i]]=max(mxval[dp[i]],a[i]);
            }
        }
    }

    
    for(int i=1;i<=n;++i){
        if(vis[i]&&num[dp[i]]==1) printf("%d ",ans-1);
        else printf("%d ",ans);
    }
}

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙大学ccsu_deer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值