模拟赛 yjqb

对于这种“不能交叉”的条件,不是很好处理。那么就考虑一下dp

dp[i][j]表示,考虑A中用前i个,考虑连接B中用前j个,最大匹配。(类似LCS的DP)

转移:dp[i][j]=max(dp[i][j-1],dp[i-1][j])当li<=j<=ri时,dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1)

这样可以保证一定不会交叉,而且不会共用匹配点

O(N^2)

看起来非常无法优化。而且我们还没有好好利用区间连边的特点。

观察转移的特点,

一个发现是,这是一个前缀取max,并且某些dp数值的位置+1

由于要和前面的i取max,

那么,把函数键值化,

对于考虑到第i个点,

图像是:

一个分段函数!

考虑如果新加入一个点i的话,在此基础上造成什么影响。

为了方便理解,考虑用滚动数组

dp[j]=max(dp[j],dp[j-1]+1)

这个dp[j]可以直接理解为上一次留下的dp值。(当然转移是倒序循环)

dp[j]=max(dp[j-1],dp[j])这个要正序循环

发现,对于分段函数造成的影响,是一段区间!

由于dp[j]=max(dp[j],dp[j-1]+1),注意每个分段函数的左端点是不会变化的。

并且,+1的转移都是dp[j]=max(dp[j],dp[j-1]+1)

所以,分段函数的落差都恰好是1!!!

如果可以维护好分段函数,那么最后最高的函数就是ans

怎么维护?

发现这个函数的变化,本质上是把更新区间中涉及到的分段函数向右移动一步,再向上移动一步得到的新的图像!

于是可以打标记了!

这个函数的区间提取,如果用线段树做的话,提取会非常麻烦。而且左移上移怎么处理?!?!

这么灵活的移动,只能交给平衡树了!

ywy_c_asm的大力讨论法:

用三元组[l,r,val]表示每个分段函数的左右端点和高度(函数值)

很多麻烦的事情:

1.边界涉及到函数分离,函数合并。

2.边界可能是某些函数的左端点,

3.和后面的合并?没有后继怎么办?

4.[L,R]只有一个分段函数?要特判

5.[L,R]有两个分段函数?由于不能直接把后面的函数合并到前驱再--r那么简单(其实好像可以?)反正特判比较保险

6.[L,R]有多个分段函数?这时候就要区间打标记了。

7.merge函数那个并入哪一个?

8.split函数,从哪里断开?剩下的l,r是什么?

还有一些splay的基本操作(我写的splay)

1.pre,bac前驱后继,记得pushdown

2.kth,记得pushdown

3.tag的标记打好。

4.左右位置放上空节点方便提取区间。

。。。。。。。。。

还有一堆细节

。。。。。。。。。

放上代码:

大概4+4+4=12种讨论?

删掉注释250行左右

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define ls t[x].ch[0]
#define rs t[x].ch[1]
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=100000+5;
const int nd=3;
const int st=1;
int n;
struct node{
    int l,r,v;
    int fa,ch[2];
    int tag;
    node(){}
    node(int ll,int rr,int vv){
        l=ll,r=rr,v=vv;
        ch[0]=ch[1]=0;
        fa=0;
        tag=0;
    }
    void op(){
        cout<<" left "<<l<<" right "<<r<<" val "<<v<<endl;
        cout<<" father "<<fa<<" son1 "<<ch[0]<<" son2 "<<ch[1]<<" tag "<<tag<<endl;
    }
}t[N];
int cnt;
int rt;
void tag(int x,int c){
    t[x].tag+=c;
    t[x].v+=c;
    t[x].l+=c;
    t[x].r+=c;
}
void pushdown(int x){
    if(!t[x].tag) return;
//    cout<<" pushdown "<<x<<" "<<t[x].tag<<endl;
    tag(ls,t[x].tag);
    tag(rs,t[x].tag);
    t[x].tag=0;
}
void rotate(int x){
    int y=t[x].fa,d=t[y].ch[1]==x;
    t[t[y].ch[d]=t[x].ch[!d]].fa=y;
    t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x;
    t[t[x].ch[!d]=y].fa=x;
}
void splay(int x,int f){
    while(t[x].fa!=f){
        int y=t[x].fa,z=t[y].fa;
        if(z!=f){
            rotate((t[z].ch[0]==y)&&(t[y].ch[0]==x)?y:x);
        }
        rotate(x);
    }
    if(f==0) rt=x;
}
int pre(int x){
    splay(x,0);
    x=t[x].ch[0];
    if(!x) return -1;
    pushdown(x);
    while(t[x].ch[1]) {
        x=t[x].ch[1];
        pushdown(x);
    }
    return x;
}
int bac(int x){
//    cout<<" fin bac "<<x<<" "<<t[x].ch[0]<<endl;
    splay(x,0);
    x=t[x].ch[1];
//    cout<<" ch[1] "<<x<<" "<<t[x].ch[0]<<endl;
    if(!x) return -1;
    pushdown(x);
    while(t[x].ch[0]) {
        x=t[x].ch[0];
        pushdown(x);
    }
    return x; 
}
int kth(int k){
//    cout<<" find kth "<<k<<endl;
    int x=rt;
    while(x){
        pushdown(x);
    //    cout<<" xx "<<x<<endl;t[x].op();
        if(t[x].l<=k&&k<=t[x].r) return x;
        else if(t[x].l>k) x=t[x].ch[0];
        else if(t[x].r<k) x=t[x].ch[1];
    }
    return -1;//warning !!!
}
void merge(int x,int y){
    //cout<<" merge "<<x<<" "<<y<<endl;
    splay(x,0);splay(y,x);
    t[t[x].ch[1]=t[y].ch[1]].fa=x;
    t[x].r=t[y].r;
}
void split(int x,int l,int r){
    ++cnt;
    t[cnt]=node(l,r,t[x].v);
    t[t[cnt].ch[1]=t[x].ch[1]].fa=cnt;
    t[cnt].fa=x;
    t[x].ch[1]=cnt;
    t[x].r=l-1;//warning!!
}
void wrk(int L,int R){
//    cout<<" wrking ---------------------"<<L<<" "<<R<<endl;
    int lc=kth(L),rc=kth(R);
    //cout<<" lc "<<lc<<endl;t[lc].op();
    //cout<<" rc "<<rc<<endl;t[rc].op();
    if(lc==rc){
    //    cout<<" Sol 1*****"<<endl;
        if(L==R){
        //    cout<<" 1.1%%"<<endl;
            if(t[lc].l==L){
//                int pr=pre(lc);
//                if(pr==st){
//                    int bc=bac(lc);
//                    if(bc==nd){
//                        ++t[lc].v;
//                    }else{
//                        merge(lc,bc);
//                        
//                    }
//                }
                return;
            }
            else if(t[lc].r==L){
                int bc=bac(lc);
                if(bc==nd){//las cur
                    split(lc,L,R);
                    ++t[cnt].v;
                }else{
                    t[bc].l=L;
                    t[lc].r=L-1;
                }
                return;
            }else{
                //cout<<" 1.1.3$$$ "<<endl;
                int bc=bac(lc);
            //    cout<<" bc "<<bc<<endl;
                if(bc==nd){
                    split(lc,L,t[lc].r);
                    ++t[cnt].v;
                }else{
                    t[bc].l=L;
                    t[lc].r=L-1;
                }
                return;
            }
        }else{
            if(t[lc].l==L){
                int bc=bac(lc);
                if(bc==nd){//las cur
                    split(lc,t[lc].l+1,t[lc].r);
                    ++t[cnt].v;
                }else{
                    t[bc].l=t[lc].l+1;
                    t[lc].r=t[lc].l;
                }
                return;
            }else{
                int bc=bac(lc);
                if(bc==nd){//las cur
                    split(lc,L,t[lc].r);
                    ++t[cnt].v;
                }else{
                    t[bc].l=L;
                    t[lc].r=L-1;
                }
                return;
            }
        }
        return;
    }
    int pr=pre(rc);
    if(pr==lc){
        if(t[lc].l==L&&t[rc].l==R){
            t[lc].r=L;
            t[rc].l=L+1;
        }else if(t[lc].l==L){
            int bc=bac(rc);
            if(bc==nd){
                split(rc,t[rc].l+1,t[rc].r);
                ++t[cnt].v;
            }else{
                t[bc].l=t[rc].l+1;
                t[rc].r=t[rc].l;
            }
            t[rc].l=L+1;
            t[lc].r=L;
        }else if(t[rc].l==R){
            t[rc].l=L;
            t[lc].r=L-1;
        }else{
            int bc=bac(rc);
            if(bc==nd){
                split(rc,t[rc].l+1,t[rc].r);
                ++t[cnt].v;
            }else{
                t[bc].l=t[rc].l+1;
                t[rc].r=t[rc].l;
            }
            t[rc].l=L;
            t[lc].r=L-1;
        }
    }
    else{
        //cout<<" Sol 3*******"<<endl;
        if(t[rc].l==R){
            merge(pr,rc);
            rc=pr;
            t[rc].r--;
        }else{
        //    cout<<" 3.1.2$$$"<<endl;
            int bc=bac(rc);
        //    cout<<" bac "<<bc<<endl;
            if(bc==nd){
                t[rc].r--;
            }else{
                merge(rc,bc);
                t[rc].r--;
            }
        }
        
        if(t[lc].l==L){
            split(lc,L,t[lc].r);
            t[lc].r=L;
            lc=cnt;
        }else{
            split(lc,L-1,t[lc].r);
            t[lc].r=L-1;
            lc=cnt;
        }
        
        int LL=pre(lc),RR=bac(rc);
        
        
        splay(LL,0);splay(RR,LL);
        //cout<<" LL "<<LL<<endl;t[LL].op();
        
    //    cout<<" RR "<<RR<<endl;t[RR].op();
        tag(t[RR].ch[0],1);
    }
}
int calc(){
    splay(nd,0);
    int cur=pre(nd);
    return t[cur].v;
}
int main(){
    rd(n);
    rt=1;
    t[++cnt]=node(-1,-1,0);
    t[cnt].fa=0;
    t[cnt].ch[1]=cnt+1;++cnt;
    
    t[cnt]=node(0,n,0);
    t[cnt].fa=1;
    t[cnt].ch[1]=cnt+1;++cnt;
    
    t[cnt]=node(n+1,n+1,0);
    t[cnt].fa=2;
    
//    cout<<t[1].fa<<" "<<t[1].ch[0]<<" "<<t[1].ch[1]<<endl;
//    cout<<t[2].fa<<" "<<t[2].ch[0]<<" "<<t[2].ch[1]<<endl;
//    cout<<t[3].fa<<" "<<t[3].ch[0]<<" "<<t[3].ch[1]<<endl;
    
    int L,R;
    for(reg i=1;i<=n;++i){
        rd(L);rd(R);
        wrk(L,R);
    //    cout<<" after wrk "<<cnt<<endl;
    }
    printf("%d
",calc());
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/24 13:54:20
*/
View Code

太TMD麻烦了。。。。。(要不是为了锻炼码力才不写)

发现之前的方法麻烦的地方在于,维护[l,r]这个区间要来回讨论。函数值+1要讨论。左端点要讨论。split,merge要讨论。。。

落差都是1,怎么利用呢?

用一些点来维护分段函数!

每个点表示这个分段函数的左端点。维护点的横坐标

修改的时候,

[l,r)的左端点集体右移,加入一个l点,把>=r最小的点删除。没了。

原因是,我们不记录高度,一个函数值左边的点的个数,就是函数值的大小!

所以,插入一个点相当于对后面的所有点高度+1,只需要将点向右平移。

同时,代替合并函数值的是,把>=r的点删除掉。

一个关键点在r的位置,并不能+1,所以是开区间。

最后点的个数就是函数值!

总结:

1.DP首先要想到。观察转移,得到分段函数的性质

2.分段函数的移动,可以模式化,所以可以打标记。

3.平衡树维护分段函数,以及一些小技巧。

原文地址:https://www.cnblogs.com/Miracevin/p/10171156.html