题意简述:
给你长度为 n n 的序列,序列中的每个元素 有一个区间限制 [li,ri] [ l i , r i ] ,你从中选出一个子序列,并给它们标号 xi x i ,要求满足 ∀i<j ∀ i < j , xi<xj x i < x j ,且 ∀i ∀ i , xi∈[li,ri] x i ∈ [ l i , r i ] 。
问满足条件子序列的长度最长为多少?
1≤n≤3×105 1 ≤ n ≤ 3 × 10 5 , 1≤li≤ri≤109 1 ≤ l i ≤ r i ≤ 10 9 。
解题报告:
根据题意我们可以直接列出动态规划的式子,
记
f[i][j]
f
[
i
]
[
j
]
为当前在第
i
i
个元素的位置,已经取了 个元素时,最后一个取的元素的
x
x
最小是多少。
于是则有:
第一行表示,元素 i+1 i + 1 不在子序列中,直接转移过去;
第二行表示,元素 i+1 i + 1 在子序列中,则要求当前的 f[i][j]<ri+1 f [ i ] [ j ] < r i + 1 ,那么 xi+1 x i + 1 肯定越小越好,则为 max{f[i][j]+1,li+1} max { f [ i ] [ j ] + 1 , l i + 1 } 。
上面的式子可以用滚动数组将第一维优化了,即:
我们挖掘一下式子的本质,看一看是如何进行优化:
首先这个式子一定是严格递增的(显然);
那么这个式子相当于在做这样一件事:
对于所有的
li+1≤f[j]<ri+1
l
i
+
1
≤
f
[
j
]
<
r
i
+
1
,
f[j]+1→f[j+1]
f
[
j
]
+
1
→
f
[
j
+
1
]
,
对于最大的
j
j
满足 ,则
li→f[j+1]
l
i
→
f
[
j
+
1
]
。
所以看到这是不是有什么想法了?
我们只需要找出在
f[j]
f
[
j
]
在
li+1≤f[j]<ri+1
l
i
+
1
≤
f
[
j
]
<
r
i
+
1
,把它们的值
+1
+
1
,数组下标也
+1
+
1
;
再找到第一个最大的
j
j
满足 ,把
li
l
i
赋值给
f[j+1]
f
[
j
+
1
]
,即可。
对的没错,一切的一切都只需要一个优美的 Splay or Treap S p l a y o r T r e a p 。
#include <cstdio>
#include <cstring>
#define Null b
#define R register
const int Inf = 2147483647;
int n;
struct Data{ int Id, val, tag1, tag2; Data *Son[2], *Pre; } b[600010], *root = Null; int tot;
struct SplayTree
{
void Pushdown(R Data *Now)
{
Now->Id += Now->tag1; Now->val += Now->tag2;
Now->Son[0]->tag1 += Now->tag1; Now->Son[0]->tag2 += Now->tag2;
Now->Son[1]->tag1 += Now->tag1; Now->Son[1]->tag2 += Now->tag2;
Now->tag1 = Now->tag2 = 0;
}
void Preview(R Data *Now)
{
if(Now->Pre != Null) Preview(Now->Pre);
Pushdown(Now);
}
void Rotate(R Data *Now)
{
R Data *f = Now->Pre;
R int d = (f->Son[1] == Now);
(f->Son[d] = Now->Son[!d])->Pre = f;
if(f->Pre != Null) f->Pre->Son[f->Pre->Son[1] == f] = Now;
Now->Pre = f->Pre;
(Now->Son[!d] = f)->Pre = Now;
}
void Splay(R Data *Now, R Data *Fa = Null)
{
Preview(Now);
while(Now->Pre != Fa)
{
if(Now->Pre->Pre != Fa)
Rotate((Now->Pre->Pre->Son[0] == Now->Pre) ^ (Now->Pre->Son[0] == Now) ?
Now : Now->Pre);
Rotate(Now);
}
Fa == Null ? root = Now : 0;
}
Data *Build(R int l, R int r)
{
if(l > r) return Null;
R int mid = l + r >> 1;
R Data *Now = b + ++tot;
b[++tot] = (Data){l == 1 ? 1 : Inf, 0};
(Now->Son[0] = Build(l, mid - 1))->Pre = Now;
(Now->Son[1] = Build(mid + 1, r))->Pre = Now;
return Now;
}
Data *Find_le(R Data *Now, R int Pos)
{
R Data *tmp = Null, *last = Null;
while(Now != Null)
{
Pushdown(Now); last = Now;
if(Now->val == Pos) tmp = Now;
if(Now->val > Pos) Now = Now->Son[0];
else tmp = Now, Now = Now->Son[1];
}
if(last != Null) Splay(last);
return tmp;
}
Data *Find_ge(R Data *Now, R int Pos)
{
R Data *tmp = Null, *last = Null;
while(Now != Null)
{
Pushdown(Now); last = Now;
if(Now->val == Pos) tmp = Now;
if(Now->val < Pos) Now = Now->Son[1];
else tmp = Now, Now = Now->Son[0];
}
if(last != Null) Splay(last);
return tmp;
}
Data *Find(R Data *Now, R int Pos)
{
Pushdown(Now);
if(Now->Id == Pos || Now == Null) return Now;
if(Now->Id < Pos) return Find(Now->Son[1], Pos);
else return Find(Now->Son[0], Pos);
}
void Delect(R Data *Now)
{
if(Now == Null) return ;
Splay(Now);
if(Now->Son[1] != Null)
{
R Data *tmp = Now->Son[1];
while(tmp->Son[0] != Null) tmp = tmp->Son[0];
Splay(tmp); Splay(Now, tmp);
(Now->Pre->Son[0] = Now->Son[0])->Pre = Now->Pre;
}
else (root = Now->Son[0])->Pre = Null;
}
Data *Add(R Data *Now, R int Id, R int val, R Data *Fa = Null)
{
if(Now == Null) b[++tot] = (Data){Id, val, 0, 0, {Null, Null}, Null}, root = b + tot;
else
{
while(Now != Null)
{
Pushdown(Now);
if(Now->Son[Now->Id < Id] == Null)
{
b[++tot] = (Data){Id, val, 0, 0, {Null, Null}, Now};
Now->Son[Now->Id < Id] = b + tot;
break;
}
else Now = Now->Son[Now->Id < Id];
}
Splay(Now);
}
}
int GetAns(R Data *Now)
{
Pushdown(Now);
if(Now->Son[1] != Null) return GetAns(Now->Son[1]);
return Now->Id;
}
} e;
int main()
{
b[0] = (Data){0, 0, 0, 0, {Null, Null}, Null};
scanf("%d", &n);
e.Add(root, 0, 1);
for(R int i = 1; i <= n; i++)
{
R int l, r, t1 = 0, t2 = 1;
scanf("%d %d", &l, &r);
R Data *A = e.Find_le(root, l - 1), *B = e.Find_ge(root, r + 1), *C = Null;
if(A->Id > B->Id - 1 && B != Null && A != Null) t2 = -1;
if(A != Null)
{
t1 = A->Id + 1, t2 = l + 1;
C = e.Find(root, t1);
if(C != Null && C->val <= l) t2 = C->val;
}
if(B == Null && A == Null) root->tag1++, root->tag2++;
else if(B == Null) e.Splay(A), A->Son[1]->tag1++, A->Son[1]->tag2++;
else if(A == Null) e.Splay(B),B->Son[0]->tag1++, B->Son[0]->tag2++;
else e.Splay(B), e.Splay(A, B), A->Son[1]->tag1++, A->Son[1]->tag2++;
e.Delect(B);
if(~t2) e.Add(root, t1, t2);
}
printf("%d\n", e.GetAns(root));
return 0;
}