Boring Class
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 354 Accepted Submission(s): 75
Here is the problem:
Give you two sequences L1,L2,...,Ln and R1,R2,...,Rn .
Your task is to find a longest subsequence v1,v2,...vm satisfies
v1≥1 , vm≤n , vi<vi+1 .(for i from 1 to m - 1)
Lvi≥Lvi+1 , Rvi≤Rvi+1 (for i from 1 to m - 1)
If there are many longest subsequence satisfy the condition, output the sequence which has the smallest lexicographic order.
1≤n≤50000
Both of the following two lines contain n integers describe the two sequences.
1≤Li,Ri≤109
Output m integers in the next line.
5 5 4 3 2 1 6 7 8 9 10 2 1 2 3 4
5 1 2 3 4 5 1 1
求最长不降子序列。满足 Li >= Li+1 and Ri <= Ri+1
题解说可以用分治和树套树写,我先用set写,发现set不能完成二维上的查询。然后用splay写,超时了。
树套树还不懂怎么做到的空间,这里只会n^2的空间的方法。
本文采用分治方法:
由于保证最小字典序,则从后往前做,把大小关系取反,求最长上升子序列。
如果求出最长上升子序列,那么从左到右。如果最长长度为ans,遇到第一个满足len = ans的点输出,然后ans--,记录上一次输出的点p,再遇到len = ans的点q,满足p.l >=q.l and p.r <= q.r 就输出q点位置,然后ans--以此类推。
分治求最长上升子序列的方法。
------------------------------点已经逆序排序了
遇到[L,R]区间,先处理左区间,
LLLLLLRRRRRR
然后将R的id值 取反,R只能从L中传递过去更新len值,
用新数组保存这些点,按L从小到大排序(逆序,所以从小到大),如果L相同则id值大的排前面。
从左到右枚举:
遇到id值 > 0的点p在线段树中p.r 中插入该点的len值(取最大的)
遇到id值 < 0 的点p查询满足r>=q.r中len最大的值,+1即更新len[p.id]
然后处理右区间
这样做,R先从L转移了。如果R要从R中点转移,因为最后才处理右区间,所以也会计算到的。
ps:线段树懒标记,方便情况,r要离散化,不然线段树保存不了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
#define maxn 200007
int lz[maxn],lc[maxn],rc[maxn],val[maxn];
int treecnt = 0;
void build(int u,int l,int r){
if(l == r) return ;
lc[u] = treecnt++;
rc[u] = treecnt++;
int mid = (l+r)/2;
build(lc[u],l,mid);
build(rc[u],mid+1,r);
}
void push_down(int u){
if(lz[u]){
lz[u] = 0;
if(rc[u] != 0)
lz[rc[u]] = 1,val[rc[u]] = 0;
if(lc[u] != 0)
lz[lc[u]] = 1,val[lc[u]] = 0;
}
}
void update(int u){
val[u] = max(val[rc[u]],val[lc[u]]);
}
int query(int u,int l,int r,int pp){
push_down(u);
if(l >= pp) return val[u];
int mid = (l+r)/2;
if(pp >= mid+1)
return query(rc[u],mid+1,r,pp);
return max(query(lc[u],l,mid,pp),query(rc[u],mid+1,r,pp));
}
void add(int u,int l,int r,int pp,int num){
if(l == r){
if(lz[u]) val[pp] = 0;
lz[u] = 0;
val[u] = max(val[u],num);
return ;
}
push_down(u);
int mid = (l+r)/2;
if(pp >= mid+1)
add(rc[u],mid+1,r,pp,num);
else add(lc[u],l,mid,pp,num);
update(u);
}
struct Node{
int l,r,id;
};
Node p[maxn];
Node q[maxn];
Node w[maxn];
int comp(Node a,Node b){
return a.r < b.r;
}
int comp1(Node a,Node b){
return a.id > b.id;
}
int comp3(Node a,Node b){
if(a.l == b.l) return a.id > b.id;
return a.l < b.l;
}
int len[maxn];
void fenzhi2(int l,int r){
if(r < l) return ;
sort(q+l,q+r+1,comp3);
for(int i = l;i <= r; i++){
int id = abs(q[i].id);
if(q[i].id < 0){
len[id] = max(len[id],1+query(1,1,50000,q[i].r));
}
else {
add(1,1,50000,q[i].r,len[id]);
}
}
lz[1] = 1;
val[1] = 0;
}
void fenzhi(int l,int r){
if(l == r) {
len[p[l].id] = max(len[p[l].id],1);
return ;
}
int mid = (l+r)/2;
fenzhi(l,mid);
for(int i = l;i <= r; i++){
q[i] = p[i];
if(i > mid) q[i].id *= -1;
}
fenzhi2(l,r);
fenzhi(mid+1,r);
}
int comp2(Node a,Node b){
return a.id < b.id;
}
int main(){
int n;
//freopen("1009.in","r",stdin);
//freopen("10091.out","w",stdout);
while(scanf("%d",&n)!=EOF){
for(int i = 0;i < n;i++){
scanf("%d",&p[i].l);
p[i].id = i+1;
}
for(int i = 0;i < n; i++){
scanf("%d",&p[i].r);
}
sort(p,p+n,comp);
int cnt = 1,r = p[0].r;
for(int i = 0;i < n; i++){
if(p[i].r == r) p[i].r = cnt;
else {
r = p[i].r;
p[i].r = ++cnt;
}
}
sort(p,p+n,comp1);
memset(len,0,sizeof(len));
memset(lz,0,sizeof(lz));
memset(val,0,sizeof(val));
memset(lc,0,sizeof(lc));
memset(rc,0,sizeof(rc));
treecnt = 2;
build(1,1,50000);
fenzhi(0,n-1);
int ans =0;
for(int i = 1;i <= n; i++){
ans = max(ans,len[i]);
}
sort(p,p+n,comp2);
Node x;
int flag = 1,be=-1;
printf("%d\n",ans);
for(int i = 0;i < n; i++){
if(ans == len[p[i].id]){
if(be == -1 || (p[i].l <= p[be].l && p[i].r >= p[be].r)){
if(flag == 0) printf(" ");
printf("%d",p[i].id);
flag = 0;
ans--;
be = i;
}
}
}
printf("\n");
}
return 0;
}