模拟赛 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] ...
随机推荐
- Oracle 修改数据库表数据提交之后进行回滚
--查看历史数据 select * from test1 as of timestamp to_timestamp('2018-12-23 14:41:00', 'yyyy-mm-dd hh24:mi ...
- 如何让pl/sql developer记住密码,实现快速登录
前两天,有同事使用plsql的时候,切换数据库的时候需要不断的重复输入密码,这样太麻烦了. 下面,我这里说下如何的实现plsql不需要输入密码就能快速登录的方法: 1.一开始登录,首先像往常那样输入密 ...
- ActiveMQ入门案例-生产者代码实现
<–start–> 使用Java程序操作ActiveMQ生产消息,代码的复杂度较高,但也没有默写下来的必要. 开发ActiveMQ首先需要导入activemq-all.jar包,如果是ma ...
- Docker入门了解一下(第一篇)
最近在学docker.k8s什么的,看得脑子有点乱.从来没弄过在linux上搭建一个分布式的环境,所以对这些不太了解,还是从最简单的地方剖析吧. Docker学习传送:http://www.ityou ...
- leetcode资料整理
注:借鉴了 http://m.blog.csdn.net/blog/lsg32/18712353 在Github上提供leetcode有: 1.https://github.com/soulmachi ...
- python爬虫之git的使用
一.简单认识: 1.初始化文件夹为版本控制文件夹,首先建立一个文件夹,进入这个文件夹以后输入git init初始化这个文件夹. 2.Git几种位置概念 1.本地代码:本地更改完代码以后,虽然是存放在g ...
- python设计模式第二十五天【访问者模式】
1.应用场景 (1)将数据和行为进行分离,不同的角色具有不同的行为 2.代码实现
- Java多线程之Join方法阻塞线程
package org.study2.javabase.ThreadsDemo.status; /** * @Auther:GongXingRui * @Date:2018/9/19 * @Descr ...
- yolo算法解析
- 【C/C++】龙格库塔+亚当姆斯求解数值微分初值问题
/* 解数值微分初值问题: 龙格-库塔法求前k个初值 + 亚当姆斯法 */ #include<bits/stdc++.h> using namespace std; double f(do ...