「Codeforces 809D」Hitchhiking in the Baltic States

CF 809D

题意简述
给你长度为 n 的序列,序列中的每个元素 i 有一个区间限制 [ l i , r i ] ,你从中选出一个子序列,并给它们标号 x i ,要求满足 i < j x i < x j ,且 i x i [ l i , r i ]
问满足条件子序列的长度最长为多少?
1 n 3 × 10 5 1 l i r i 10 9

解题报告:
根据题意我们可以直接列出动态规划的式子,
f [ i ] [ j ] 为当前在第 i 个元素的位置,已经取了 j 个元素时,最后一个取的元素的 x 最小是多少。
于是则有:

f [ i ] [ j ] f [ i + 1 ] [ j ]
i f   f [ i ] [ j ] < r i + 1 , max { f [ i ] [ j ] + 1 , l i + 1 } f [ i + 1 ] [ j + 1 ]

第一行表示,元素 i + 1 不在子序列中,直接转移过去;
第二行表示,元素 i + 1 在子序列中,则要求当前的 f [ i ] [ j ] < r i + 1 ,那么 x i + 1 肯定越小越好,则为 max { f [ i ] [ j ] + 1 , l i + 1 }

上面的式子可以用滚动数组将第一维优化了,即:

i f   f [ j ] < r i + 1 max { f [ j ] + 1 , l i + 1 } f [ j + 1 ]

我们挖掘一下式子的本质,看一看是如何进行优化:

首先这个式子一定是严格递增的(显然);

那么这个式子相当于在做这样一件事:
对于所有的 l i + 1 f [ j ] < r i + 1 f [ j ] + 1 f [ j + 1 ]
对于最大的 j 满足 f [ j ] < l i + 1 ,则 l i f [ j + 1 ]

所以看到这是不是有什么想法了?
我们只需要找出在 f [ j ] l i + 1 f [ j ] < r i + 1 ,把它们的值 + 1 ,数组下标也 + 1
再找到第一个最大的 j 满足 f [ j ] < l i + 1 ,把 l i 赋值给 f [ j + 1 ] ,即可。

对的没错,一切的一切都只需要一个优美的 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;
}

猜你喜欢

转载自blog.csdn.net/steaunk/article/details/79040006