【bzoj4553】【Tjoi2016】【Heoi2016】【序列】【树套树】【线段树套线段树】
2016-07-12 11:11
429 查看
题目大意
给出长度为n的序列a,有m次变化。每次变化把a[x]改成b[x],一个位可以多次修改,变化相对独立。选出最长字串,在m次变化与原串中都是不下降的。
题解
通过分析可知,设f[i]为前i位中一定选了i所形成的最长不下降字串的长度,f[i]可以转移到f[j] (j>i)当且仅当满足三个条件。a[i]<=a[j]。max(b[i])<=a[j]。a[i]<=min(b[j])。把a[i]也当作b[i],则要满足max(b[i])<=a[j]。a[i]<=min(b[j])。我们可以O(n^2)地dp。
其实我们只用满足两个关键字都小于或等于当前两个关键字就可以转移,我们考虑线段树套线段树。外层线段树代表第一关键字,内层代表第二关键字,把f值放进去,单点修改,区间查询就可以了。
详细点就是外层的每个区间[l,r]都拥有一颗关于第二关键字的线段树,每个包含第一关键字的[l,r]都需要按第二关键字在自己的线段树上插入f值。再详细就是看代码了。
其实这题可以用cdq分治,或k-d tree,或其他树套树来做。
code
#include<set> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define fo(i,j,k) for(int i=j;i<=k;i++) #define fd(i,j,k) for(int i=j;i>=k;i--) using namespace std; int const maxn=100000; int n,m,pon=1,a[maxn+10],mx[maxn+10],mi[maxn+10],son[maxn*108+10][3]; void change2(int now,int l,int r,int mxi,int fi){ int m=(l+r)/2; son[now][2]=max(son[now][2],fi); if(l==r)return; else if(mxi<=m){ if(!son[now][0])son[now][0]=++pon; change2(son[now][0],l,m,mxi,fi); }else{ if(!son[now][1])son[now][1]=++pon; change2(son[now][1],m+1,r,mxi,fi); } } void change(int now,int l,int r,int ai,int mxi,int fi){ int m=(l+r)/2; if(!son[now][2])son[now][2]=++pon; change2(son[now][2],1,maxn+1,mxi,fi); if(l==r)return; else if(ai<=m){ if(!son[now][0])son[now][0]=++pon; change(son[now][0],l,m,ai,mxi,fi); }else{ if(!son[now][1])son[now][1]=++pon; change(son[now][1],m+1,r,ai,mxi,fi); } } int get2(int now,int l,int r,int a0,int ai){ int m=(l+r)/2; if((l==a0)&&(r==ai))return son[now][2]; else if(ai<=m){ if(!son[now][0])return 0; return get2(son[now][0],l,m,a0,ai); }else{ int tmp=0; if(son[now][1])tmp=get2(son[now][1],m+1,r,m+1,ai); if(son[now][0])tmp=max(tmp,son[son[now][0]][2]); return tmp; } } int get(int now,int l,int r,int mi0,int mii,int a0,int ai){ int m=(l+r)/2; if((l==mi0)&&(r==mii)){ if(!son[now][2])return 0; return get2(son[now][2],1,maxn+1,a0,ai); } else if(mii<=m){ if(!son[now][0])return 0; return get(son[now][0],l,m,mi0,mii,a0,ai); }else{ int tmp=0; if(son[now][1])tmp=get(son[now][1],m+1,r,m+1,mii,a0,ai); if(son[now][0]&&son[son[now][0]][2])tmp=max(tmp,get2(son[son[now][0]][2],1,maxn+1,a0,ai)); return tmp; } } int main(){ //freopen("len.in","r",stdin); //freopen("len.out","w",stdout); freopen("d.in","r",stdin); freopen("d.out","w",stdout); scanf("%d%d",&n,&m); fo(i,1,n)scanf("%d",&a[i]),a[i]++,mx[i]=mi[i]=a[i]; fo(i,1,m){ int x,y;scanf("%d%d",&x,&y);y++; mx[x]=max(mx[x],y); mi[x]=min(mi[x],y); } int ans=1; change(1,1,maxn+1,1,1,0); fo(i,1,n){ int f; ans=max(ans,f=get(1,1,maxn+1,1,mi[i],1,a[i])+1); change(1,1,maxn+1,a[i],mx[i],f); } printf("%d",ans); return 0; }
相关文章推荐
- /etc/passwd 和 /etc/shadow 文件内容及其解释
- ubuntu完美搭建git服务器
- 【打CF,学算法——五星级】CodeForces 478D (dp计数)
- Leetcode 3. Longest Substring Without Repeating Characters (Medium) (cpp)
- 使用Coookie实现浏览器显示上次的登录时间
- Android快速批量多渠道包的“蛋生”
- 线程并发共享资源同步原理解析1
- java基本数据类型与包装类之间的转换
- idea15导入项目
- c++继承
- 大数据相关学习内容记录
- mybatis 一对一映射
- 中间人攻击——ARP欺骗的原理、实战及防御
- oracle RAC 如何增加一个ip监听
- 乐观锁
- linux下离线安装mysql
- 实现在父页面不刷新情况下显示子页面传递的信息
- 我国在线教育行业现状及布局分析
- zabbix3.0.3 139短信报警
- 计算机程序的结构和解释公开课