题意简述:
给你长度为 的序列,序列中的每个元素 有一个区间限制 ,你从中选出一个子序列,并给它们标号 ,要求满足 , ,且 , 。
问满足条件子序列的长度最长为多少?
, 。
解题报告:
根据题意我们可以直接列出动态规划的式子,
记
为当前在第
个元素的位置,已经取了
个元素时,最后一个取的元素的
最小是多少。
于是则有:
第一行表示,元素 不在子序列中,直接转移过去;
第二行表示,元素 在子序列中,则要求当前的 ,那么 肯定越小越好,则为 。
上面的式子可以用滚动数组将第一维优化了,即:
我们挖掘一下式子的本质,看一看是如何进行优化:
首先这个式子一定是严格递增的(显然);
那么这个式子相当于在做这样一件事:
对于所有的
,
,
对于最大的
满足
,则
。
所以看到这是不是有什么想法了?
我们只需要找出在
在
,把它们的值
,数组下标也
;
再找到第一个最大的
满足
,把
赋值给
,即可。
对的没错,一切的一切都只需要一个优美的 。
#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;
}