[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\n",f[num[i]]);
}

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

原文地址:https://www.cnblogs.com/mastervan/p/9426889.html

时间: 2024-10-11 17:08:51

[DP][二分]JZOJ 3467 最长上升子序列的相关文章

【二分】【最长上升子序列】HDU 5489 Removed Interval (2015 ACM/ICPC Asia Regional Hefei Online)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5489 题目大意: 一个N(N<=100000)个数的序列,要从中去掉相邻的L个数(去掉整个区间),使得剩余的数最长上升子序列(LIS)最长. 题目思路: [二分][最长上升子序列] 首先,假设去掉[i,i+m-1]这L个数,剩余的LIS长度为max(i左端最后一个不大于a[i+m]的LIS长度+a[i+m]开始到最后的LIS长度). 所以,我们从n到1逆向先求最长下降子序列的长度f[i],就可以知

【动态规划】【二分】【最长上升子序列】Vijos P1028 魔族密码

题目链接: https://vijos.org/p/1028 题目大意: 给N个字符串(N<=2000),求能组成词链的单词最多有几个. 如果在一个由一个词或多个词组成的表中,除了最后一个以外,每个单词都被其后的一个单词所包含 即前一个单词是后一个单词的前缀,则称词表为一个词链.例如下面单词组成了一个词链: i int integer 但下面的单词不组成词链: integer intern 题目思路: [动态规划][二分][最长上升子序列] 二分查找最长可达的长度. 1 // 2 //by co

【动态规划】【二分】CDOJ1006 最长上升子序列

最长上升子序列. 要求输出字典序最小解. 就在更新答案的时候记录一下前驱.容易发现记录的这个玩意实际上形成了一个森林. #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int T,n,a[1010],b[1010],pre[1010],anss[1010],e,pos[1010]; int main(){ int x; scanf("%d",&am

DP简单问题联系--最长递增子序列+最长公共子序列等

今天重温了一下dp问题,发现自己两个礼拜不写题目就什么都不会了...心态爆炸,感觉去考试怕是要gg了... 不过今天总结一下写的题目,全部都是基础的dp问题 第一个是 求最长不下降子序列的长度 第一行为n,表示n个数 第二行n个数 最长不下降子序列的长度 N小于5000 for  each  num  < =maxint 样例输入 3 1 2 3 样例输出 3 // // Created by 陈平 on 2018/7/8. // #include "iostream" usin

算法练习之DP 求LCM (最长公共子序列)

1. 对于序列x[1,i]和y[1,j],推导递推公式1.a 如果当前元素相同,那么就将当前最大相同数+12.b 如果当前元素不同,那么就把当前最大相同数"传递"下去 因此递推公式为: x[i] == y[j] : dp[i][j] = Max(dp[i-1][j-1],dp[i][j-1],dp[i-1][j]) + 1 x[i] != y[j] : dp[i][j] = Max(dp[i][j-1],dp[i-1][j]) 由于x[i]!=y[j]的情况不难可以对x[i]==y[j

[DP][二分]JZOJ 3463 军训

Description HYSBZ 开学了!今年HYSBZ 有n 个男生来上学,学号为1-n,每个学生都必须参加军训.在这种比较堕落的学校里,每个男生都会有Gi 个女朋友,而且每个人都会有一个欠扁值Hi.学校为了保证军训时教官不会因为学生们都是人生赢家或者是太欠扁而发生打架事故,所以要把学生们分班,并做出了如下要求: 1.分班必须按照学号顺序来,即不能在一个班上出现学号不连续的情况. 2.每个学生必须要被分到某个班上. 3.每个班的欠扁值定义为该班中欠扁值最高的那名同学的欠扁值.所有班的欠扁值之

最长公共子序列(仅借助数组dp本身在O(m+n)时间内构造最长公共子序列)

算法课上机作业,想复杂了.. 给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列. 改进LCS函数,不使用数组b而仅借助数组c本身在O(m+n)时间内构造最长公共子序列. 原来的代码: void LCSLength(char *x ,char *y,int m,int n, int **c, int **b) {        int i ,j;        for (i = 1; i <= m; i++) c[i][0] = 0;       

longestIncreasingSequence最长上升子序列问题

1 package dp; 2 3 /** 4 * 最长上升子序列问题 5 */ 6 public class LongestIncreasingSubsequence 7 { 8 /** 9 * 原始dp 10 * @param arr 11 * @return 12 */ 13 public static int maxLength(int[] arr) 14 { 15 int[] len = new int[arr.length] ; //以i为结尾的最长上升子序列 16 int[] ma

最长递增子序列(LIS)求解

问题描述 最长递增子序列也称 "最长上升子序列",简称LIS ( longest increasing subsequence).设L=<a1,a2,-,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lis=<ak1,ak2,-,akm>,其中k1<k2<-<km且ak1<ak2<-<akm.求最大的m值. 如:求一个一维数组arr[i]中的最长递增子序列的长度,如在序列{ 7, 1, 6, 5, 3, 4, 8