题目链接
这题自己瞎搞贪心写了半天,没做出来。
献上题解:
什么是最小链覆盖?
献上百度知识点:
相应的,深度了解最小链覆盖:最小链覆盖
有用的大概就是下面这个:
什么是最长反链呢?
在充分了解了最小链覆盖和最长反链后,为什么两者是等价的呢?
看证明最长反链=最小链覆盖(证明+解析)。这个证明我就没有细细的看了。。
在充分了解了最长反链和最小链覆盖后,开始理解这题。。
这题不就是一个无向图(递减),且要 求最小链覆盖。
然后集合反链性质 简单想想就知道了 数字递减构成的图反链==数字不递减构成的图(举几个样例就可以了: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);
}
}