对于这种“不能交叉”的条件,不是很好处理。那么就考虑一下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. display设置弹性盒布局

    转自:http://blog.csdn.net/itbwy/article/details/52648711 网页布局(layout)是CSS的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖  ...

  2. Linux 的相关操作

    切换权限   在linux环境下,用户之前的切换使用 “su - name,若要切换到root下面,则使用sudo su 命令即可. 在linux下安装软件,经常就是装完后不知道装到哪里去了 (201 ...

  3. python爬虫之初始Selenium

    1.初始 Selenium[1]  是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE(7, 8, 9, 10, 11),Moz ...

  4. python爬虫之requests的基本使用

    简介 Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库,Requests它会比urllib更加方便,可以节约我们大量的工作. 一 ...

  5. drf开发中常见问题

    开发常见问题及解决 问题: 一.本地系统不能重现的bug 二.api接口出错不能及时的发现或难找到错误栈 三.api文档管理问题 四.大量的url配置造成url配置越来越多难以维护 五.接口不及时去更 ...

  6. Ajax之Jquery封装使用举例2(Json和JsonArray处理)

    本例主要使用ajax进行异步数据请求,并针对返回数据为json和jsonarray类型的数据处理. 本例中只有前端的代码,后端代码不是本文重点,故省略. 后端接口返回数据为: Json: {" ...

  7. web.xml中三种通配符及匹配规则

    一.url-pattern的三种写法 1.精确匹配.以”/”开头,加上servlet名称:    /ad  ; 2.路径匹配.以”/”开头,加上通配符”*” :    /*  ; 3.扩展名匹配.以通 ...

  8. ASP.NET MVC和Web API中的Angular2 - 第2部分

    下载源码 内容 第1部分:Visual Studio 2017中的Angular2设置,基本CRUD应用程序,第三方模态弹出控件 第2部分:使用Angular2管道进行过滤/搜索,全局错误处理,调试客 ...

  9. centOS7 下配置和启动maria数据库

    从最新版本的linux系统开始,默认的是 Mariadb而不是mysql! 使用系统自带的repos安装很简单: yum install mariadb mariadb-server systemct ...

  10. mac上录制无声mp3

    想要录制音频很多方法,要无声音频 你可以使用调试软件 那么mac的录制音频,可以用这个软件 将耳机插头插在话筒插孔内,录制出来了m4a文件, 进入转换网站 开始转换下载