[DP][二分]JZOJ 3467 最长上升子序列

Posted mastervan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[DP][二分]JZOJ 3467 最长上升子序列相关的知识,希望对你有一定的参考价值。

Description

维护一个序列,使它可以进行下面两种操作:

1.在末尾添加一个数字x

2.将整个序列变成第x次操作后的样子

在每次操作后,输出当前序列的最长上升子序列的长度

序列初始时为空
 

Input

输入文件lis.in的第一行有一个正整数n,表示操作个数。接下来n行每行有两个整数op,x。如果op为0,则表示添加x这个数字;如果op为1,则表示回到第x次操作之后。

Output

对于每次操作,在输出文件lis.out中输出一个答案,表示当前最长上升子序列的长度
 

Sample Input

5
0 2
0 0
1 0
1 0
0 5

Sample Output

1
1
0
0
1
【样例说明】
第一次操作后,序列为 2
第二次操作后,序列为2 0
第三次操作后,序列为(空)
第四次操作后,序列为(空)
第五次操作后,序列为 5
 

Data Constraint

30%的数据  n<=1000

另外20%的数据没有第二个操作

80%的数据 n<=200000

100%的数据 n<=500000且所有输入的数字都是长整型范围内的非负整数

分析

我们可以容易发现这个题的数据输入呈一个树形,但是我们无法每次都对一条链求最长上升子序列。

然后想到DFS可以重置一些东西,记录一些相关的变化量再退回即可。

(然后传统DFS居然爆栈了?)手写一个while版的DFS= =

技术分享图片
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=500001;
struct Edge {
    int u,v,nx;
}g[N];
struct D {
    int j,k,u,v;
    bool b;
}stk[N];
int top;
int cnt,list[N];
int f[N],d[N],num[N],w[N];
int pcnt,mlen;
int n;

void Add(int u,int v) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt;
}

void Dfs(int u) {
    stk[top].u=u;
    while (1) {
        while (!list[stk[top].u]&&top>0) 
        {
            top--;
            if (stk[top].b) d[mlen--]=0;
            else if (stk[top].j>0) d[stk[top].j]=stk[top].k;
        };
        if (!list[stk[top].u]) break;
        int i=list[stk[top].u];
        list[stk[top].u]=g[i].nx;
        stk[top].v=g[i].v;
        stk[top].b=0;
        if (d[mlen]<w[stk[top].v]) {
            d[++mlen]=w[stk[top].v];
            stk[top].b=1;
        }
        else {
            stk[top].j=lower_bound(d,d+mlen+1,w[stk[top].v])-d;
            if (stk[top].j>0) stk[top].k=d[stk[top].j],d[stk[top].j]=w[stk[top].v];
        }
        f[stk[top].v]=mlen;
        top++;
        stk[top].u=stk[top-1].v;
    }
}

void Init() {
    scanf("%d",&n);
    num[0]=0;
    for (int i=1;i<=n;i++) {
        int order,p;
        scanf("%d%d",&order,&p);
        if (order)
        num[i]=num[p];
        else {
            num[i]=++pcnt;
            Add(num[i-1],num[i]);
            w[pcnt]=p;
        }
    }
    d[0]=-2147483647;mlen=0;
}

void Print() {
    for (int i=1;i<=n;i++)
    printf("%d
",f[num[i]]);
}

int main() {
    freopen("lis.in","r",stdin);
    freopen("lis.out","w",stdout);
    Init();
    Dfs(0);
    Print();
    fclose(stdin);fclose(stdout);
}
View Code

 






























以上是关于[DP][二分]JZOJ 3467 最长上升子序列的主要内容,如果未能解决你的问题,请参考以下文章

300. 最长上升子序列(动态规划,二分查找)

最长上升子序列(贪心+二分)

1134 最长上升子序列 (序列型 DP)

POJ 3903 Stock Exchange 最长上升子序列模板题

最长上升子序列 (LIS) 详解+例题模板 (全)(转)

最长上升子序列 (LIS) 详解+例题模板 (全)(转)