这道题放了很久还是回来补了
D 天才麻将少女KPM SRM 07
背景&&描述
天才麻将少女KPM立志要在日麻界闯出一番名堂。
KPM上周叒打了n场麻将,但她这次又没控分,而且因为是全市参与的麻将大赛,所以她的名次范围是0..10^5。
名次可能等于0是因为KPM那场没去打= =
没去打就意味着无限的可能性。
KPM叒想要让自己的名次严格递增。为了避免被妹子怀疑,她只能把没打的比赛的名次改成T..R中的整数
当然,n场全部严格递增是很难做到的。你只需要求出可能的最长递增子序列长度就好了。
输入格式
第一行三个整数n,T,R。
第二行n个整数,表示n场的排名。
输出格式
可能的最长递增子序列长度。
样例输入
5 1 4
3 0 5 9 2
样例输出
4
数据范围与约定
- 对于100%的数据:
先贴一波大爷的题解
做严格上升 f【i】表示当前以i结尾的最大答案
a【j】!=0照常 a【j】==0的话就有f【i】=max(f【i】,f【i-1】)+1;(L<=i<=R)
f【i-1】一定>=f【i】所以相当于+1后整体平移一格 用平衡树就可以实现了
所以实际涉及到的操作有区间更新 单点插入和删除
————————————————————————————————————
接下来自然就是我自己的写法咯‘
f【w】表示考虑到当前点的以w结尾的最长上升字序列的长度
其实f数组可以写成f【x】【i】 表示到第x个数以i结尾的最长上升子序列
但是明显我们用到的只有上一个点的信息 所以完全可以压成一维
实际上开成两维空间也接受不了
但是啊 因为我懒得弄区间操作所以我写成了差分 即树上维护的是f【i】-f【i-1】
这样转换之后就只涉及到单点插入修改
在涉及到的区间左端点位置插入一个值 删除右端点右侧的第一个1
至于为什么请听我细细道来...
首先我们考虑f【i-1】+1和f【i】的关系
0<=f【i】-f【i-1】<=1 这个很好证明吧
f【i】是一定>f【i-1】的 如果f【i】-f【i-1】大于1 那么序列不可能严格递增
所以相当于直接将区间+1后挪动一个位置 将取max直接改为赋值
因为我维护的是差分 就相当于在左端点处插入一个点,删去右端点后面的第一个1
至于左右端点 如果a【i】是0 自然是 l——r 否则就是那一个点咯(一个点也是区间)
至于为什么要删除 因为他能够影响到的就只能到右端点后第一个1 这个自己画图模拟一下咯
所以我们平衡树需要维护的就是区间是否有1 以及子树size
splay删除实在太恶心 所以我写了fhq treap
单点插入容易 分裂合并一波就好了
删除右端点后的第一个点就需要用到我们维护的——区间内是否有1
先把右端点后的区间分裂出来 考虑当前点x
if x的左子树有1 那么就往左子树跑
else if x本身有1 就删除x
else 往右子树跑
至于怎么删除x 把他的左右子树合在一起(merge)就好了
最后的答案就统计一下到0——a【i】的最大值之间有多少个1就行了
可能讲完这些还不是很清楚 不然来模拟一波样例吧
5 1 4
3 0 5 9 2
f【】的变化
000000000
001111111
111222222
111233333
111233334
122233334
f【】的差分变化
000000000
001000000
100100000
100110000000110001
110010001
100110001
这样思路应该清晰了
顺便口胡一句 fhq treap 真的难调.......
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=1e5+;
int read(){
int ans=,f=,c=getchar();
while(c<''||c>''){if(c=='-') f=-; c=getchar();}
while(c>=''&&c<=''){ans=ans*+(c-''); c=getchar();}
return ans*f;
}
struct node{
node *l,*r;
int h,f,sz,rnd;
void init(){h=; sz=; rnd=rand();}
void up(){
sz=; f=h;
if(l) sz+=l->sz,f=f|(l->f);
if(r) sz+=r->sz,f=f|(r->f);
}
void split(node*&lw,node*&rw,int k){
if(!this){lw=; rw=; return ;}
int ls=l?l->sz:;
if(k<=ls){
l->split(lw,l,k);
rw=this;
}
else{
r->split(r,rw,k-ls-);
lw=this;
}
up();
}
}tr[*M],*rt;
node* merge(node*a,node*b){
if(!a) return b;
if(!b) return a;
if(a->rnd>b->rnd){
a->r=merge(a->r,b);
a->up();
return a;
}{
b->l=merge(a,b->l);
b->up();
return b;
}
}
void modify(node*&x){
if(!x||!(x->f)) return ;
if(x->l&&(x->l->f)) modify(x->l);
else if(x->h==){
if(x->l&&x->r) x=merge(x->l,x->r);
else if(x->l) x=x->l;
else x=x->r;
return ;
}
else modify(x->r);
x->up();
}
int mx,n,l,r,k,cnt=M,ans;
int query(node *x){
if(!x) return ;
int sum=x->h;
if(x->l) sum+=query(x->l);
if(x->r) sum+=query(x->r);
return sum;
}
int main()
{
// freopen("input.in","r",stdin);
for(int i=;i<=M;i++) tr[i].init(),rt=merge(rt,tr+i);
n=read(); l=read(); r=read();
for(int i=;i<=n;i++){
k=read();
if(k) mx=max(mx,k);else mx=max(mx,r);
tr[++cnt].init();
tr[cnt].h=tr[cnt].f=;
node *p1,*p2,*p3;
if(!k){
rt->split(p2,p3,r-);
p2->split(p1,p2,l-);
modify(p3);
rt=merge(merge(p1,tr+cnt),merge(p2,p3));
}
else{
rt->split(p1,p2,k-);
modify(p2);
rt=merge(merge(p1,tr+cnt),p2);
}
}
node *p1,*p2;
rt->split(p1,p2,mx);
ans=query(p1);
printf("%d\n",ans);
return ;
}