对于这种“不能交叉”的条件,不是很好处理。那么就考虑一下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=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=+;
const int nd=;
const int st=;
int n;
struct node{
int l,r,v;
int fa,ch[];
int tag;
node(){}
node(int ll,int rr,int vv){
l=ll,r=rr,v=vv;
ch[]=ch[]=;
fa=;
tag=;
}
void op(){
cout<<" left "<<l<<" right "<<r<<" val "<<v<<endl;
cout<<" father "<<fa<<" son1 "<<ch[]<<" son2 "<<ch[]<<" 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=;
}
void rotate(int x){
int y=t[x].fa,d=t[y].ch[]==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[]==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[]==y)&&(t[y].ch[]==x)?y:x);
}
rotate(x);
}
if(f==) rt=x;
}
int pre(int x){
splay(x,);
x=t[x].ch[];
if(!x) return -;
pushdown(x);
while(t[x].ch[]) {
x=t[x].ch[];
pushdown(x);
}
return x;
}
int bac(int x){
// cout<<" fin bac "<<x<<" "<<t[x].ch[0]<<endl;
splay(x,);
x=t[x].ch[];
// cout<<" ch[1] "<<x<<" "<<t[x].ch[0]<<endl;
if(!x) return -;
pushdown(x);
while(t[x].ch[]) {
x=t[x].ch[];
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[];
else if(t[x].r<k) x=t[x].ch[];
}
return -;//warning !!!
}
void merge(int x,int y){
//cout<<" merge "<<x<<" "<<y<<endl;
splay(x,);splay(y,x);
t[t[x].ch[]=t[y].ch[]].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[]=t[x].ch[]].fa=cnt;
t[cnt].fa=x;
t[x].ch[]=cnt;
t[x].r=l-;//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-;
}
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-;
}
return;
}
}else{
if(t[lc].l==L){
int bc=bac(lc);
if(bc==nd){//las cur
split(lc,t[lc].l+,t[lc].r);
++t[cnt].v;
}else{
t[bc].l=t[lc].l+;
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-;
}
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+;
}else if(t[lc].l==L){
int bc=bac(rc);
if(bc==nd){
split(rc,t[rc].l+,t[rc].r);
++t[cnt].v;
}else{
t[bc].l=t[rc].l+;
t[rc].r=t[rc].l;
}
t[rc].l=L+;
t[lc].r=L;
}else if(t[rc].l==R){
t[rc].l=L;
t[lc].r=L-;
}else{
int bc=bac(rc);
if(bc==nd){
split(rc,t[rc].l+,t[rc].r);
++t[cnt].v;
}else{
t[bc].l=t[rc].l+;
t[rc].r=t[rc].l;
}
t[rc].l=L;
t[lc].r=L-;
}
}
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-,t[lc].r);
t[lc].r=L-;
lc=cnt;
} int LL=pre(lc),RR=bac(rc); splay(LL,);splay(RR,LL);
//cout<<" LL "<<LL<<endl;t[LL].op(); // cout<<" RR "<<RR<<endl;t[RR].op();
tag(t[RR].ch[],);
}
}
int calc(){
splay(nd,);
int cur=pre(nd);
return t[cur].v;
}
int main(){
rd(n);
rt=;
t[++cnt]=node(-,-,);
t[cnt].fa=;
t[cnt].ch[]=cnt+;++cnt; t[cnt]=node(,n,);
t[cnt].fa=;
t[cnt].ch[]=cnt+;++cnt; t[cnt]=node(n+,n+,);
t[cnt].fa=; // 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=;i<=n;++i){
rd(L);rd(R);
wrk(L,R);
// cout<<" after wrk "<<cnt<<endl;
}
printf("%d\n",calc());
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/12/24 13:54:20
*/

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

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

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

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

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

修改的时候,

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

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

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

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

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

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

总结:

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

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

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

模拟赛 yjqb的更多相关文章

  1. NOIP模拟赛20161022

    NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...

  2. NOI模拟赛 Day1

    [考完试不想说话系列] 他们都会做呢QAQ 我毛线也不会呢QAQ 悲伤ING 考试问题: 1.感觉不是很清醒,有点困╯﹏╰ 2.为啥总不按照计划来!!! 3.脑洞在哪里 4.把模拟赛当作真正的比赛,紧 ...

  3. NOIP第7场模拟赛题解

    NOIP模拟赛第7场题解: 题解见:http://www.cqoi.net:2012/JudgeOnline/problemset.php?page=13 题号为2221-2224. 1.car 边界 ...

  4. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

  5. NOIP模拟赛 by hzwer

    2015年10月04日NOIP模拟赛 by hzwer    (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...

  6. 小奇模拟赛9.13 by hzwer

    2015年9月13日NOIP模拟赛 by hzwer    (这是小奇=> 小奇挖矿(explo) [题目背景] 小奇要开采一些矿物,它驾驶着一台带有钻头(初始能力值w)的飞船,按既定路线依次飞 ...

  7. PKUSC 模拟赛 day1 下午总结

    下午到了机房之后又困又饿,还要被强行摁着看英文题,简直差评 第一题是NOIP模拟赛的原题,随便模拟就好啦 本人模拟功力太渣不小心打错了个变量,居然调了40多分钟QAQ #include<cstd ...

  8. [GRYZ]寒假模拟赛

    写在前面 这是首次广饶一中的OIERS自编自导,自出自做(zuo)的模拟赛. 鉴于水平气压比较低,机(wei)智(suo)的WMY/XYD/HYXZC就上网FQ下海找了不少水(fei)题,经过他们优( ...

  9. BZOJ2741: 【FOTILE模拟赛】L

    2741: [FOTILE模拟赛]L Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1170  Solved: 303[Submit][Status] ...

随机推荐

  1. vue页面传参和接参

    https://blog.csdn.net/zhouzuoluo/article/details/81259298(copy) js** this.$router.push({ name: 'Flow ...

  2. Linux基础学习笔记6-SHELL编程

    编程基础 程序:指令+数据 程序编程风格: 过程式:以指令为中心,数据服务于指令 对象式:以数据为中心,指令服务于数据 shell程序:提供了编程能力,解释执行 编程基本概念: 顺序执行:循环执行:选 ...

  3. 在 Ubuntu14.04 上搭建 Spark 2.3.1(latest version)

    搭建最新的 Spark 2.3.1 . 首先需要下载最新版 jdk .目前 2.3.1 需要 8.0 及其以上 jdk 才可以允许. 所以如果你没有 8.0  jdk 安装好了之后会报错.不要尝试安装 ...

  4. lombok标签之@Data @AllArgsConstructor @@NoArgsConstructor -如何去除get,set方法。@Data注解和如何使用,lombok

    在代码中我们可以只加上标签@Data 而不用get,set方法: val : 和 scala 中 val 同名, 可以在运行时确定类型; @NonNull : 注解在参数上, 如果该类参数为 null ...

  5. WEB测试重点--(转载)

    1.功能测试: 所实现的功能是否和需求一致: js错误 页面链接错误-空链接.死链接.错误链接 按钮无效 未实现功能 报错提示信息不准确或不友好 数据库访问错误 sql注入 文档上传下载问题 -未实现 ...

  6. PHP namespace、require、use区别

    假设 有文件a.php 代码 <?php class a{//类a public function afun()//函数afun { echo "aaaa"; } } ?&g ...

  7. C-Lodop提示“网页还没下载完毕,请稍等一下再操作.”

    该提示在Lodop旧版本中是: 提示"WebSocket没准备好,点确定继续",提示“C-Lodop没准备好”,新版本修改了该提示的描述“网页还没下载完毕,请稍等一下再操作.”,让 ...

  8. php重定向http请求

    302  临时重定向 301  永久重定向     (  302 和 301  的区别主要在于搜索引擎,搜索引擎一般不会抓取临时重定向的页面  ) 301 和302 适用于 普通的GET 请求: 如果 ...

  9. 目前市场上有些什么样的数据库管理系统(DBMS),它们都有什么特点?它们之间的优缺点有什么?它们的使用场合分别是?

    1 要求 目前市场上有些什么样的数据库管理系统(DBMS),它们都有什么特点?它们之间的优缺点有什么?它们的使用场合分别是? 1.1 目前市场上有些什么样的数据库管理系统(DBMS) 目前市场上的数据 ...

  10. IntelliJ IDEA default settings 全局默认设置

    可以通过以下两个位置设置IDEA的全局默认设置: 以后诸如默认的maven配置就不需要每次都重复配置了?