做这个题之前,建议先做 POJ 2828. 附题解 POJ 2828题解
Another LIS
There is a sequence firstly empty. We begin to add number from 1 to N to the sequence, and every time we just add a single number to the sequence at a specific position. Now, we want to know length of the LIS (Longest Increasing Subsequence) after every time's add.
Input
An integer T (T <= 10), indicating there are T test cases.
For every test case, an integer N (1 <= N <= 100000) comes first, then there are N numbers, the k-th number Xk means that we add number k at position Xk (0 <= Xk <= k-1).See hint for more details.
Output
For the k-th test case, first output "Case #k:" in a separate line, then followed N lines indicating the answer. Output a blank line after every test case.
Sample Input
1
3
0 0 2
Sample Output
Case #1:
1
1
2
Hint
In the sample, we add three numbers to the sequence, and form three sequences.
a. 1
b. 2 1
c. 2 1 3
题意:有一个起初是空的序列。我们开始在序列中添加从1到N的数字,每次我们只在特定位置向序列添加一个数字。现在,我们想知道每次添加后LIS(最长上升子序列)的长度。
思路:首先就是如 POJ 2828 一样,解决插队完成后的序列是什么 这个问题。 接着需要解决的问题才是 每次添加后的LIS 长度是多少
对于每次添加后的LIS 长度,可以这样解决:
如序列 4 1 3 2,可以按这个序列建树,维护的是一个最大值,表示以 a[i] 结尾的 LIS 长度,如 3 :表示序列以 3 结尾的 LIS 长度 。查询的时候就应该查询的是 区间 (1, a[i] - 1) 的最大值,然后 + 1。
这样就可以得到 以 每个数为结尾的 LIS 长度。
由于 插入的时候是按顺序添加 1 ~ n 的数,所以最后 再在 以 a[i] 与 a[i] - 1 两个为结尾的LIS中取个max 就是答案。
AC代码:
#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs (i << 1) + 1
#define INT(t) int t; scanf("%d",&t)
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn],wz[maxn];
int Left[maxn << 2];
void build(int l,int r,int i){
if(l == r)
Left[i] = 1;
else {
int mid = (l + r) >> 1;
build(l,mid,ls);
build(mid + 1,r,rs);
Left[i] = Left[ls] + Left[rs];
}
}
void query(int l,int r,int i,int pos,int val){
if(l == r){
-- Left[i];
wz[l] = val;
return ;
}
int mid = (l + r) >> 1,ans;
if(Left[ls] >= pos) query(l,mid,ls,pos,val);
else query(mid + 1,r,rs,pos - Left[ls],val);
Left[i] = Left[ls] + Left[rs];
}
int LIS[maxn << 2];
int ans[maxn];
void updateLIS(int l,int r,int pos,int val,int i){
if(l == r){
LIS[i] = val;
return ;
}
int mid = (l + r) >> 1;
if(pos <= mid) updateLIS(l,mid,pos,val,ls);
if(pos > mid) updateLIS(mid + 1,r,pos,val,rs);
LIS[i] = max(LIS[ls],LIS[rs]);
}
int queryLIS(int l,int r,int ql,int qr,int i){
if(ql > qr) return 0;
if(ql <= l && r <= qr){
return LIS[i];
}
int mid = (l + r) >> 1;
int ans = -1;
if(ql <= mid) ans = max(ans,queryLIS(l,mid,ql,qr,ls));
if(qr > mid) ans = max(ans,queryLIS(mid + 1,r,ql,qr,rs));
return ans;
}
int main() {
int t; scanf("%d",&t);
int Cas = 1;
while(t --){
int n; scanf("%d",&n);
for(int i = 1;i <= n;++ i)
scanf("%d",&a[i]);
build(1,n,1);
for(int i = n;i > 0;-- i){
query(1,n,1,a[i] + 1,i);
}
clr(LIS,0);
clr(ans,0);
for(int i = 1;i <= n;++ i){
int tmp = queryLIS(1,n,1,wz[i] - 1,1) + 1;
ans[wz[i]] = max(tmp,ans[wz[i] - 1]);
updateLIS(1,n,wz[i],tmp,1);
}
printf("Case #%d:\n",Cas ++);
for(int i = 1;i <= n;++ i){
ans[i] = max(ans[i],ans[i - 1]);
printf("%d\n",ans[i]);
}
printf("\n");
}
return 0;
}