Nonsense Time HDU - 6635 (思维 + LIS + 路径标记)

13 篇文章 2 订阅
3 篇文章 0 订阅

题意:

链接:HDU - 6635

给你 n 不同的且属于 [1, n] 的数列 p,然后在给你 n 个数 k1 - kn,代表 n 次操作,开始数列是被冻住的只有解冻的数才可以使用,按照给的n次操作,每次操作解冻一个位置,并输出当前所有被解冻的数的最长上升子序列的长度。

解题思路:

有句非常关键的语句:It is guaranteed that p1,p2,...,pn and k1,k2,...,kn are generated randomly。所以我们倒着求,把解冻换成冰冻,每冰冻一个在ki位置的数,判断是否在当前最长上升子序列里面,如果不在直接输出当前的最长上升子序列的长度,否则重新计算最长上升子序列,因为数据是随机给出的,求最长上升子序列的次数的期望是只有 √n 次的,每次计算是 nlogn ,总的时间复杂度为: n√nlogn。其中需要标记最长上升子序列的路径以便于判断冰冻的元素是都是在当前的最长上升子序列里面。

这代码写的有点太丑,主要这个题目的思想很好,下次要记住这个随机生成,其实就表明他不会有特殊样例来卡你。

AC代码:

#include<bits/stdc++.h>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
using namespace std;

int n, len, cur_ans;
int a[maxn], del[maxn], dp[maxn];
int vis[maxn], pre[maxn], ans[maxn];
list<int> lst;
list<int> :: iterator iter;

int solve(int x)
{
    if(x == -1)
    {
        for(int i = 1; i <= n; i++) pre[i] = i;
        int k = 1;
        dp[k] = *lst.begin();
        iter = lst.begin();
        for(++iter; iter != lst.end(); iter++)
        {
            if(dp[k] < *iter)
            {
                pre[*iter] = dp[k];
                dp[++k] = *iter;
            }
            else
            {
                int loc = lower_bound(dp + 1, dp + 1 + k, *iter) - dp;
                if(loc > 1)
                {
                    pre[*iter] = dp[loc - 1];
                }
                dp[loc] = *iter;
            }
        }
        memset(vis, 0, sizeof(vis));
        int x = dp[k]; vis[x] = 1;
        while(x != pre[x])
        {
            x = pre[x];
            vis[x] = 1;
        }
        return cur_ans = k;
    }
    if(vis[ a[x] ] == 0)
    {
        iter = lst.begin();
        while(iter != lst.end())
        {
            if(*iter == a[x])
            {
                lst.erase(iter);
                break;
            }++iter;
        }
        return cur_ans;
    }
    else
    {
        for(int i = 1; i <= n; i++) pre[i] = i;
        iter = lst.begin();
        while(iter != lst.end())
        {
            if(*iter == a[x])
            {
                lst.erase(iter);
                break;
            }++iter;
        }

        int k = 1;
        iter = lst.begin();
        dp[k] = *iter;

        for(++iter; iter != lst.end(); iter++)
        {
            if(dp[k] < *iter)
            {
                pre[*iter] = dp[k];
                dp[++k] = *iter;
            }
            else
            {
                int loc = lower_bound(dp + 1, dp + 1 + k, *iter) - dp;
                if(loc > 1)
                {
                    pre[*iter] = dp[loc - 1];
                }
                dp[loc] = *iter;
            }
        }
        memset(vis, 0, sizeof(vis));
        int x = dp[k]; vis[x] = 1;
        while(x != pre[x])
        {
            x = pre[x];
            vis[x] = 1;
        }
        return cur_ans = k;
    }
}

int main()
{
    int T; scanf("%d", &T); while(T--)
    {
        lst.clear();
        len = 0;
        scanf("%d", &n);
        up(i, 1, n) scanf("%d", &a[i]), lst.push_back(a[i]);
        up(i, 1, n) scanf("%d", &del[i]);
        ans[n] = solve(-1);
        for(int i = n; i > 1; i--)
        {
            ans[i - 1] = solve(del[i]);
        }
        for(int i = 1; i <= n; i++)
            printf("%d%c", ans[i], i == n ? '\n' : ' ');
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值