纸飞机【最小链覆盖等于最长反链】

牛客挑战赛36 C 题目链接


  题目想知道的是最长严格下降子序列的最少个数用以覆盖完N个数。也就是最小链覆盖问题了。

  那么,我们反过来想,最长反链,我们求得最长不递减序列的个数,如题,“2 4 3 1 5”中“2 4 5”就是最长的不递减系列了,那么,从2、4、5中各个点起飞是不是就可以覆盖完整个链了,所以这就是最长反链了。

  然后,现在就是想知道,我们删除一个元素会不会使得最长反链的长度下降一个?也就是使得答案变成“ans - 1”?那么,我们就是要看这一位是否对这个最长反链造成了贡献,或者换句话说,能不能有另外的元素可以替换它目前的位置。

  那么,计算这层贡献,肯定是从最后的一位开始算的,我们先给“N+1”(end位置)先附上一个比其余所有元素都大的值(离散化之后)。我们现在从后往前查询到每一位,(因为我们查它在反链中的所能表示的位置是从前往后查的),然后,如果说我们这一位会对后面造成贡献,那么也就是说我们可以查到一个在最长反链中表示的位置是它“+1”并且值还需要大于等于它的数的位置。

  每一个元素在最长反链中的贡献:如果说它在最长反链中的位置是pos_{i},那么现在要看pos_{i + 1}这一位的最大值是不是比它大或者等于,那么统计它的贡献,在pos_{i}这一位,它可以存在,如果说有多个可以在pos_{i}这一位存在的,说明它可以被替换。

  然后最后的答案一定是ans或者是ans-1,ans代表了不删除任意元素时候的最长反链的长度。是否“-1”取决于它是否对最长反链有贡献,并且是否不可被替换

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e6 + 7;
int N, h[maxN], lsan[maxN], _UP;
int tim[maxN] = {0}, deep[maxN], del[maxN] = {0}, mx[maxN] = {0};
bool flag[maxN] = {false};  //有没有用到对应位置的元素
struct BIT_Tree_MAX
{
    int tree[maxN] = {0};
    inline void update(int x, int val) { while(x <= _UP) { tree[x] = max(tree[x], val); x += lowbit(x); } }
    inline int query(int x) { int sum = 0; while(x) { sum = max(sum, tree[x]); x -= lowbit(x); } return sum; }
    inline void re_update(int x, int val) { while(x) { tree[x] = max(tree[x], val); x -= lowbit(x); } }
    inline int re_query(int x) { int sum = 0; while(x <= _UP) { sum = max(sum, tree[x]); x += lowbit(x); } return sum; }
} t;
int main()
{
    scanf("%d", &N);
    for(int i=1; i<=N; i++) { scanf("%d", &h[i]); lsan[i] = h[i]; }
    sort(lsan + 1, lsan + N + 1);
    _UP = (int)(unique(lsan + 1, lsan + N + 1) - lsan - 1);
    for(int i=1; i<=N; i++) h[i] = (int)(lower_bound(lsan + 1, lsan + _UP + 1, h[i]) - lsan);
    _UP++;
    int ans = 0;
    for(int i=1; i<=N; i++)
    {
        deep[i] = t.query(h[i]) + 1;
        t.update(h[i], deep[i]);
        ans = max(ans, deep[i]);
    }
    mx[ans + 1] = _UP;
    for(int i=N; i>=1; i--)
    {
        if(mx[deep[i] + 1] >= h[i])
        {
            mx[deep[i]] = max(mx[deep[i]], h[i]);
            flag[i] = true;
        }
    }
    for(int i=1; i<=N; i++) if(flag[i]) tim[deep[i]]++;
    for(int i=1, det = 0; i<=N; i++)
    {
        det = tim[deep[i]] == 1 && flag[i];
        printf("%d%c", ans - det, i == N ? '\n' : ' ');
    }
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Wuliwuliii

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

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

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

打赏作者

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

抵扣说明:

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

余额充值