模拟赛 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=;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的更多相关文章
- NOIP模拟赛20161022
NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...
- NOI模拟赛 Day1
[考完试不想说话系列] 他们都会做呢QAQ 我毛线也不会呢QAQ 悲伤ING 考试问题: 1.感觉不是很清醒,有点困╯﹏╰ 2.为啥总不按照计划来!!! 3.脑洞在哪里 4.把模拟赛当作真正的比赛,紧 ...
- NOIP第7场模拟赛题解
NOIP模拟赛第7场题解: 题解见:http://www.cqoi.net:2012/JudgeOnline/problemset.php?page=13 题号为2221-2224. 1.car 边界 ...
- contesthunter暑假NOIP模拟赛第一场题解
contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...
- NOIP模拟赛 by hzwer
2015年10月04日NOIP模拟赛 by hzwer (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...
- 小奇模拟赛9.13 by hzwer
2015年9月13日NOIP模拟赛 by hzwer (这是小奇=> 小奇挖矿(explo) [题目背景] 小奇要开采一些矿物,它驾驶着一台带有钻头(初始能力值w)的飞船,按既定路线依次飞 ...
- PKUSC 模拟赛 day1 下午总结
下午到了机房之后又困又饿,还要被强行摁着看英文题,简直差评 第一题是NOIP模拟赛的原题,随便模拟就好啦 本人模拟功力太渣不小心打错了个变量,居然调了40多分钟QAQ #include<cstd ...
- [GRYZ]寒假模拟赛
写在前面 这是首次广饶一中的OIERS自编自导,自出自做(zuo)的模拟赛. 鉴于水平气压比较低,机(wei)智(suo)的WMY/XYD/HYXZC就上网FQ下海找了不少水(fei)题,经过他们优( ...
- BZOJ2741: 【FOTILE模拟赛】L
2741: [FOTILE模拟赛]L Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 1170 Solved: 303[Submit][Status] ...
随机推荐
- display设置弹性盒布局
转自:http://blog.csdn.net/itbwy/article/details/52648711 网页布局(layout)是CSS的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖 ...
- Linux 的相关操作
切换权限 在linux环境下,用户之前的切换使用 “su - name,若要切换到root下面,则使用sudo su 命令即可. 在linux下安装软件,经常就是装完后不知道装到哪里去了 (201 ...
- python爬虫之初始Selenium
1.初始 Selenium[1] 是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE(7, 8, 9, 10, 11),Moz ...
- python爬虫之requests的基本使用
简介 Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库,Requests它会比urllib更加方便,可以节约我们大量的工作. 一 ...
- drf开发中常见问题
开发常见问题及解决 问题: 一.本地系统不能重现的bug 二.api接口出错不能及时的发现或难找到错误栈 三.api文档管理问题 四.大量的url配置造成url配置越来越多难以维护 五.接口不及时去更 ...
- Ajax之Jquery封装使用举例2(Json和JsonArray处理)
本例主要使用ajax进行异步数据请求,并针对返回数据为json和jsonarray类型的数据处理. 本例中只有前端的代码,后端代码不是本文重点,故省略. 后端接口返回数据为: Json: {" ...
- web.xml中三种通配符及匹配规则
一.url-pattern的三种写法 1.精确匹配.以”/”开头,加上servlet名称: /ad ; 2.路径匹配.以”/”开头,加上通配符”*” : /* ; 3.扩展名匹配.以通 ...
- ASP.NET MVC和Web API中的Angular2 - 第2部分
下载源码 内容 第1部分:Visual Studio 2017中的Angular2设置,基本CRUD应用程序,第三方模态弹出控件 第2部分:使用Angular2管道进行过滤/搜索,全局错误处理,调试客 ...
- centOS7 下配置和启动maria数据库
从最新版本的linux系统开始,默认的是 Mariadb而不是mysql! 使用系统自带的repos安装很简单: yum install mariadb mariadb-server systemct ...
- mac上录制无声mp3
想要录制音频很多方法,要无声音频 你可以使用调试软件 那么mac的录制音频,可以用这个软件 将耳机插头插在话筒插孔内,录制出来了m4a文件, 进入转换网站 开始转换下载