[解题报告] HDU 6635 Nonsense Time(多校 LIS 思维 暴力)

题目链接

题目大意

起初给你n的排列,然后给你一个序列b,b[i]表示第i轮位置为b[i]的数可用,问每一轮可用的数列形成的LIS(最长上升子序列)为多少。

LIS

推荐一篇特别详细特别用心的博客:
最长上升子序列 (LIS) 详解+例题模板 (全)
LIS的两种nlogn的方法都需要掌握,但是常用的是贪心+二分的这种,树状数组思想比写法重要,要善于灵活应用,特别是很类似于离散化然后添点形成大小关系从而求出某种序列的时候,要想到类比LIS的树状数组做法。

官方题解

考虑时间倒流,一开始先求出整个排列的LIS
然后考虑删点
如果删的点不在LIS中,那么我们当前轮次的LIS就是上一轮次的LIS
否则删的点在LIS中,那么我们就需要重新求一遍LIS,这里有点暴力的意思,但是由于数据随机生成,所以在LIS中的点大概有根号n个,所以其实不会超时
这里涉及到一个点是记录LIS的路径,代码中有所体现,可以当成板子背一下,其实和dp记录路径的思想是一样的,先求完然后倒推回来。
总时间复杂度 O(n * sqrt(n) * logn),14s的时间…

//#include <bits/stdc++.h>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <utility>
#include <assert.h>
#define endl '\n'
#define p_b push_back
#define e_b emplace_back
#define debug cout<<"!!!"<<endl;
//#define LOCAL
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
const static int inf = 0x3f3f3f3f;
const static ll infll = 0x3f3f3f3f3f3f3f3f;
const static int maxn = 5e4+10;

int n;
int a[maxn], b[maxn];
bool froze[maxn];
bool used[maxn];
int res[maxn];
int path[maxn];
int li[maxn];

int lis()
{
    memset(path, 0, sizeof path);
    int ret = 0;
    for(int i=1; i<=n; i++)
    {
        if(froze[a[i]])
            continue;
        //这里尽量这样写,更合理一些,li数组是下标为0开始存放贪心得到的序列
        int pos = lower_bound(li, li+ret, a[i]) - li;
        if(pos == ret)
        {
            li[ret++] = a[i];
            path[i] = ret;
        }
        else
        {
            path[i] = pos+1;//加1是下标偏移
            li[pos] = a[i];
        }
    }
    memset(used, false, sizeof used);
    int tmp = ret;
    for(int i=n; i>=1; i--)
    {
        if(froze[a[i]])
            continue;
        if(path[i] == tmp)
        {
            used[a[i]] =  true;
            tmp--;
        }
    }
    return ret;
}

int main()
{
    #ifdef Local
    freopen("1002.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    int t;
    scanf("%d", &t);
    while(t--)
    {
        memset(froze, false, sizeof froze);
        memset(res, 0, sizeof res);
        scanf("%d", &n);
        for(int i=1; i<=n; i++) scanf("%d", &a[i]);
        for(int i=1; i<=n; i++) scanf("%d", &b[i]);
        res[n] = lis();
        //cout<<res[n]<<endl;
        //debug;
        froze[a[b[n]]] = true;
        for(int i=n-1; i>=1; i--)
        {
            if(!used[a[b[i+1]]])
                res[i] = res[i+1];
            else
                res[i] = lis();
            froze[a[b[i]]] = true;
        }
        for(int i=1; i<=n; i++)
        {
            if(i == 1)
                printf("%d", res[i]);
            else
                printf(" %d", res[i]);
        }
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值