「Codeforces 809D」Hitchhiking in the Baltic States

CF 809D

題意簡述
給你長度爲 n 的序列,序列中的每個元素 i 有一個區間限制 [li,ri] ,你從中選出一個子序列,並給它們標號 xi ,要求滿足 i<jxi<xj ,且 ixi[li,ri]
問滿足條件子序列的長度最長爲多少?
1n3×1051liri109

解題報告:
根據題意我們可以直接列出動態規劃的式子,
f[i][j] 爲當前在第 i 個元素的位置,已經取了 j 個元素時,最後一個取的元素的 x 最小是多少。
於是則有:

f[i][j]f[i+1][j]
if f[i][j]<ri+1,max{f[i][j]+1,li+1}f[i+1][j+1]

第一行表示,元素 i+1 不在子序列中,直接轉移過去;
第二行表示,元素 i+1 在子序列中,則要求當前的 f[i][j]<ri+1 ,那麼 xi+1 肯定越小越好,則爲 max{f[i][j]+1,li+1}

上面的式子可以用滾動數組將第一維優化了,即:

if f[j]<ri+1max{f[j]+1,li+1}f[j+1]

我們挖掘一下式子的本質,看一看是如何進行優化:

首先這個式子一定是嚴格遞增的(顯然);

那麼這個式子相當於在做這樣一件事:
對於所有的 li+1f[j]<ri+1f[j]+1f[j+1]
對於最大的 j 滿足 f[j]<li+1 ,則 lif[j+1]

所以看到這是不是有什麼想法了?
我們只需要找出在 f[j]li+1f[j]<ri+1 ,把它們的值 +1 ,數組下標也 +1
再找到第一個最大的 j 滿足 f[j]<li+1 ,把 li 賦值給 f[j+1] ,即可。

對的沒錯,一切的一切都只需要一個優美的 Splay or Treap

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章