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