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且所有輸入的數字都是長整型範圍內的非負整數
想法:
這題前50分能水80分
f[i]表示最長上升子序列爲i,第i位最小是多少
其實我們可以把所有操作變成一棵樹,0操作就i-1與i連邊,1操作x與i連邊
然後從0開始遍歷一棵樹,1操作就什麼都不變,0操作就改變f[],並把相應的改變記錄
n太大卡dfs,猥瑣的人工棧
Code
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxN=500010;
int n,i,a[maxN],b[maxN],x,y,z,
tot,next[maxN],last[maxN],tov[maxN],
f[maxN],tail,ans[maxN],l,r,mid;
void insert(int x,int y){
tov[++tot]=y,next[tot]=last[x],last[x]=tot;
}
struct zhj{
int x,z,ch,wz,qz;
};
zhj pre[maxN];
int main(){
freopen("lis.in","r",stdin);
freopen("lis.out","w",stdout);
scanf("%d",&n);
for (i=1;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);
if (a[i]==0)
insert(i-1,i);
else insert(b[i],i);
}
ans[0]=0;
tot=0;
tail=1;
pre[1].x=0;
pre[1].z=last[0];
while (tail>0){
i=pre[tail].z;
y=tov[i];
if (y==19)
{
i=1;
}
if (a[y]==0){
if (f[tot]<b[y]){
f[++tot]=b[y];
ans[y]=tot;
pre[++tail].x=y;
pre[tail].z=last[y];
pre[tail].ch=1;
}
else{
l=1;r=tot;
while (l<r){
mid=(l+r)/2;
if (f[mid]>=b[y]) r=mid;else l=mid+1;
}
z=f[l];
f[l]=b[y];
ans[y]=tot;
pre[++tail].x=y;
pre[tail].z=last[y];
pre[tail].ch=0;
pre[tail].wz=l;
pre[tail].qz=z;
}
}
else{
ans[y]=tot;
pre[++tail].x=y;
pre[tail].z=last[y];
pre[tail].ch=2;
}
while ((pre[tail].z==0)&&(tail>=0)){
if (pre[tail].ch==1)
f[tot]=0,tot--;
if (pre[tail].ch==0)
f[pre[tail].wz]=pre[tail].qz;
tail--;
pre[tail].z=next[pre[tail].z];
}
}
for (i=1;i<=n;i++)
printf("%d\n",ans[i]);
}