Initial | data structure | ODT

Posted by master82 on Sun, 05 Dec 2021 18:20:04 +0100

"Start"

  • Why should I learn this?

    • Idle.

The default source of all codes in this article is used 「V5.2」.

"About ODT"

  • What is the use of ODT?

    • (mostly) cheat scores in DS questions with interval assignment operation, because there seem to be few questions specially designed for ODT, right? Anyway, all I know is CF896C
  • Time complexity?

    • The complexity of ODT is correctly based on random data, which must be kept in mind.

    • For all basic operations (such as \ (Assign \) and \ (Add \), the complexity of ODT implemented with set is \ (O(n \log\log n) \), while the complexity of linked list implementation is \ (O(n \log n) \), but I only use set to implement it at present(

  • matters needing attention?

    • The complexity of ODT is correctly based on the random data, the complexity of ODT is correctly based on the random data, and the complexity of ODT is correctly based on the random data. Otherwise, it is easy for the author to construct the data and let you T out.

    • Don't be partial scorecards without interval assignment.

"Realization"

The first is the core idea: merge the intervals with the same value into nodes and store them in set.

Therefore, the following structures exist to store nodes:

Node

struct Node
{
    LL l,r;
    mutable LL v;

    Node(LL l,LL r=0,LL v=0) : l(l),r(r),v(v) {}

    I bool operator < (const Node &co) const
    {
        Heriko l<co.l;
    }
};

set<Node> s; 

The purpose of mutable here is to break through the constraint of const, so that we can directly modify the value in set later, rather than take it out and throw it in after modification.

"Split"

\(Split \) is the most important operation in ODT. To put it simply, the interval \ ([l,r] \) is divided into \ ([l,pos-1] \) and \ ([pos,r] \) for our convenience.

The implementation is also very simple. We first use the lower_bound provided by set to determine the corresponding position of \ (pos \), and then delete the original interval and insert it in two halves.

I auto Split(LL pos)
{
    auto it(s.lower_bound(Node(pos)));

    if(it!=s.end() and it->l==pos)
        Heriko it;

    --it;

    if(it->r<pos)
        Heriko s.end();

    LL l(it->l),r(it->r),v(it->v);
    s.erase(it);
    s.insert(Node(l,pos-1,v));

    Heriko s.insert(Node(pos,r,v)).first;
}

In this way, all operations on the interval \ ([l,r] \) can be converted to \ ([Split(l),Split(r+1)]. \)

"Flatten Assign"

\(Assign \) is also a very important operation. It is mainly to complete the shrinking point task. It is also very simple to implement. After finding the interval, delete and insert a new one.

I void Assign(LL l,LL r,LL x)
{
    auto itr(Split(r+1)),itl(Split(l));
    s.erase(itl,itr);
    s.insert(Node(l,r,x));
}

In fact, the most basic operations are the above two. Let's expand some common operations.

"Interval plus Add"

How? Violence.

Well, yes, it's violence. After finding the corresponding interval, violence will be added(

I void Add(LL l,LL r,LL x)
{
    auto itr(Split(r+1)),itl(Split(l));

    for(auto it(itl);it!=itr;++it)
        it->v+=x;
}

"Rank"

To query the interval ranking, we first specify a structure or pair to facilitate the operation of the same number.

struct Rank
{
    LL val,cnt;

    Rank(LL val,LL cnt) : val(val),cnt(cnt) {}

    I bool operator < (const Rank &co) const
    {
        Heriko val<co.val;
    }
};

Then we use the best idea, first find the corresponding interval, then sort all the numbers, and directly find the required ranking.

I LL QueryRank(LL l,LL r,LL x)
{
    auto itr(Split(r+1)),itl(Split(l));
    vector<Rank> v;
    
    for(auto it(itl);it!=itr;++it)
        v.push_back(Rank(it->v,it->r-it->l+1));

    sort(v.begin(),v.end());
    LL i(0);

    for(;i<(LL)v.size();++i)
        if(v[i].cnt<x)
            x-=v[i].cnt;
        else
            Heriko v[i].val;

    Heriko v[i].val;
}

"Other"

In fact, you can also find the operations on ODT by observing the above. It's easy to find the corresponding interval first. All the general code frameworks are like this:

I auto Function(int l,int r,...)
{
    auto itr(Split(r+1)),itl(Split(l));
    
    ...
}

Then you can kill CF896C after knowing this.

「CF896C Code」

CI MXX(1e5+1),MOD(1e9+7);

LL n,m,seed,vmax,a[MXX];

I LL GetData()
{
    LL res(seed);
    seed=(seed*7+13)%MOD;

    Heriko res;
}

I LL FstPow(LL x,LL y,LL p)
{
    LL res(1);
    x%=p;

    while(y)
    {
        if(y&1)
            (res*=x)%=p;

        (x*=x)%=p;
        y>>=1;
    }

    Heriko res;
}

struct Node
{
    LL l,r;
    mutable LL v;

    Node(LL l,LL r=0,LL v=0) : l(l),r(r),v(v) {}

    I bool operator < (const Node &co) const
    {
        Heriko l<co.l;
    }
};

set<Node> s;

I auto Split(LL pos)
{
    auto it(s.lower_bound(Node(pos)));

    if(it!=s.end() and it->l==pos)
        Heriko it;

    --it;

    if(it->r<pos)
        Heriko s.end();

    LL l(it->l),r(it->r),v(it->v);
    s.erase(it);
    s.insert(Node(l,pos-1,v));

    Heriko s.insert(Node(pos,r,v)).first;
}

I void Assign(LL l,LL r,LL x)
{
    auto itr(Split(r+1)),itl(Split(l));
    s.erase(itl,itr);
    s.insert(Node(l,r,x));
}

I void Add(LL l,LL r,LL x)
{
    auto itr(Split(r+1)),itl(Split(l));

    for(auto it(itl);it!=itr;++it)
        it->v+=x;
}

struct Rank
{
    LL val,cnt;

    Rank(LL val,LL cnt) : val(val),cnt(cnt) {}

    I bool operator < (const Rank &co) const
    {
        Heriko val<co.val;
    }
};

I LL QueryRank(LL l,LL r,LL x)
{
    auto itr(Split(r+1)),itl(Split(l));
    vector<Rank> v;
    
    for(auto it(itl);it!=itr;++it)
        v.push_back(Rank(it->v,it->r-it->l+1));

    sort(v.begin(),v.end());
    LL i(0);

    for(;i<(LL)v.size();++i)
        if(v[i].cnt<x)
            x-=v[i].cnt;
        else
            Heriko v[i].val;

    Heriko v[i].val;
}

I LL QueryVal(LL l,LL r,LL x,LL y)
{
    auto itr(Split(r+1)),itl(Split(l));
    LL res(0);
    
    for(auto it(itl);it!=itr;++it)
        res=(res+FstPow(it->v,x,y)*(it->r-it->l+1)%y)%y;

    Heriko res;
}

S main()
{
    Files();

    fr(n),fr(m),fr(seed),fr(vmax);

    for(int i(1);i<=n;++i)
        a[i]=(GetData()%vmax)+1,s.insert(Node(i,i,a[i]));

    while(m--)
    {
        LL opt((GetData()%4)+1),l((GetData()%n)+1),r((GetData()%n)+1),x,y;

        if(l>r)
            swap(l,r);

        if(opt==3)
            x=(GetData()%(r-l+1))+1;
        else
            x=(GetData()%vmax)+1;

        if(opt==4)    
            y=(GetData()%vmax)+1;

        if(opt==1)
            Add(l,r,x);
        else if(opt==2)
            Assign(l,r,x);
        else if(opt==3)
            fw(QueryRank(l,r,x),1);
        else
            fw(QueryVal(l,r,x,y),1);
    }

    Heriko Deltana;
}

"Other examples"

After adjusting CF896C for three days, I finally found that the fast power was less than x%=p, so I did a simple ODT board problem.

"HAOI2014 POSTER"

This problem is huge, obviously, thief board.

Just flatten the interval, open a bucket and record it. Cut it directly, right.

CI MXX(1001);

struct Node
{
    int l,r;
    mutable int val;

    Node(int l,int r=0,int val=0) : l(l),r(r),val(val) {}

    I bool operator < (const Node &co) const
    {
        Heriko l<co.l;
    }
};

set<Node> s;

I auto Split(int pos)
{
    auto it(s.lower_bound(Node(pos)));

    if(it!=s.end() and it->l==pos)
        Heriko it;

    --it;

    if(it->r<pos)
        Heriko s.end();

    int l(it->l),r(it->r),v(it->val);
    s.erase(it);
    s.insert(Node(l,pos-1,v));

    Heriko s.insert(Node(pos,r,v)).first;
}

I void Assign(int l,int r,int v)
{
    auto itr(Split(r+1)),itl(Split(l));
    s.erase(itl,itr);
    s.insert(Node(l,r,v));
}

int n,m,x,y,tot,ans(-1);

bitset<MXX> vis;

S main()
{
    Files();

    fr(n),fr(m);
    s.insert(Node(1,n+1));

    while(m--)
        fr(x),fr(y),Assign(x,y,++tot);

    for(auto it(s.begin());it!=s.end();++it)
        if(!vis[it->val])
            ++ans,vis[it->val]=1;

    fw(ans,1);

    Heriko Deltana;
}

「CF343D Water Tree」

This problem is a tree problem. Compare the tree section of the board(

However, we do not write segment trees. Instead, we go directly to the ODT. After the id sequence is processed by DFS on both sides, we can operate according to the ordinary sequence.

For the second operation, we need to record the top during DFS and constantly skip top \ (Assign \) during modification.

CI MXX(5e5+5);

struct ODT
{
    int l,r;
    mutable int v;

    ODT(int l,int r=0,int v=0) : l(l),r(r),v(v) {}

    I bool operator < (const ODT &co) const
    {
        Heriko l<co.l;
    }
};

set<ODT> s;

I auto Split(int pos)
{
    auto it(s.lower_bound(ODT(pos)));

    if(it!=s.end() and it->l==pos)
        Heriko it;

    --it;

    if(it->r<pos)
        Heriko s.end();

    int l(it->l),r(it->r),val(it->v);
    s.erase(it);
    s.insert(ODT(l,pos-1,val));
    
    Heriko s.insert(ODT(pos,r,val)).first;
}

I void Assign(int l,int r,int x)
{
    auto itr(Split(r+1)),itl(Split(l));
    s.erase(itl,itr);
    s.insert(ODT(l,r,x));
}

struct Node
{
    int nex,to;
}

r[MXX<<1];

int rcnt,head[MXX];

I void Add(int x,int y)
{
    r[++rcnt]=(Node){head[x],y},head[x]=rcnt;
    r[++rcnt]=(Node){head[y],x},head[y]=rcnt;
}

int n,m,sz[MXX],dep[MXX],id[MXX],top[MXX],fa[MXX],son[MXX],tot;

void DFS1(int x,int fath)
{
    sz[x]=1,fa[x]=fath,dep[x]=dep[fath]+1;

    for(int i(head[x]);i;i=r[i].nex)
    {
        int y(r[i].to);

        if(y==fath)
            continue;

        DFS1(y,x);
        sz[x]+=sz[y];

        if(sz[y]>sz[son[x]])
            son[x]=y;
    }
}

void DFS2(int x,int tp)
{
    top[x]=tp,id[x]=++tot;

    if(son[x])
        DFS2(son[x],tp);

    for(int i(head[x]);i;i=r[i].nex)
    {
        int y(r[i].to);

        if(y==fa[x] or y==son[x])
            continue;

        DFS2(y,y);
    }
}

I void ModifyZero(int x)
{
    int tp(top[x]);

    while(tp!=1)
    {
        Assign(id[tp],id[x],0);
        x=fa[tp],tp=top[x];
    }

    Assign(id[1],id[x],0);
}

S main()
{
    Files();

    fr(n);

    for(int i(1);i<n;++i)
    {
        int x,y;
        fr(x),fr(y);
        Add(x,y);
    }

    DFS1(1,0);
    DFS2(1,1);
    s.insert(ODT(0,MXX));
    fr(m);

    while(m--)
    {
        int opt,x;
        fr(opt),fr(x);

        if(opt==1)
            Assign(id[x],id[x]+sz[x]-1,1);
        else if(opt==2)
            ModifyZero(x);
        else
            fw(Split(id[x])->v,1);
    }

    Heriko Deltana;
}

"End"

Then write this.