hdu 5324 树套树、cdq分治
2015-08-02 11:33
495 查看
一、线段树/树状数组套平衡树
题目大意是给你n个(L[i], R[i])的点,找出最长的子序列,满足L[i]递减,R[i]递增。为了看起来顺眼点,我们可以把其中一维取相反数,使得两个都找递减,或者都找递增的。
我们把L[i]取相反数,找两个都递增的。假设不考虑R[i],只考虑L[i],那么就相当于LIS问题了。
dp[i]记录以L[i]为结尾的最大长度,dp[i]=max{dp[j]}+1,其中L[j]<=L[i]。问题就在于,找到满足<=L[i]的最大值。这时候我们可以将L[i],离散化用线段树维护区间最大值。对于,dp[i]我们查询区间[1,L[i]]的最大值更新dp[i],然后将位置L[i]的最大值更新为dp[i],后面循环这一操作就能得到答案。
而这个问题还多了一维,还要处理R[i]的递增关系,那么线段树里维护不仅仅是最大值dp[i],而是需要维护(R[i],dp[i])这样的值对。对于更新dp[i]的时候,我们首先还是要先找[1,L[i]]所对应的区间节点,然后在这些节点里找到满足R[j]<=R[i]最大的dp[j]值。按照处理L的方法,我们应该用一棵平衡树来维护(R[i], dp[i])这样的键值对,来达到快速查找R[j]<=R[i]的目的。所以线段树区间里维护的不是最大值了,而是一棵平衡树,而平衡树维护的是(R[i],dp[i])键值对。
所以查找最大值就是,先找到线段树里[1,L[i]]的所有覆盖区间,然后对于每一个区间在平衡树里查找<=R[i]的最大值。复杂度是logn*logn。
总体时间复杂度O(n*logn*logn)。更新的时候,单点更新,每个点要经过logn个区间,也就是插入logn棵平衡树,所以空间复杂度是O(nlogn),平衡树开一个20*n的内存池存储。
这题要求字典序最小,可以用逆序处理。从左到右处理dp[i]得到的是以第i个为结尾的最长长度,我们想要的是开头尽可能小,那么从右到左扫就是得到以i为开头的最大长度。
平衡树之前没写过,照着模板学着敲SBT的,代码写的很冗长就不贴了。
还有这里用树状数组求最大值会比线段树快很多,1300ms和2200ms。。。
二、cdq分治
这个是看了博客 http://blog.csdn.net/u013007900/article/details/47111319 学的。模仿着写了一遍。复杂度是T(n)=T(n/2)+O(nlogn),所以是O(nlogn*logn)。其中可以优化的地方就是,每一层分治的时候所需要的排序,可以通过预先归并排序,并记录归并中每一层的排序结果来优化。用归并优化后时间从1100ms->600ms。
题目大意是给你n个(L[i], R[i])的点,找出最长的子序列,满足L[i]递减,R[i]递增。为了看起来顺眼点,我们可以把其中一维取相反数,使得两个都找递减,或者都找递增的。
我们把L[i]取相反数,找两个都递增的。假设不考虑R[i],只考虑L[i],那么就相当于LIS问题了。
dp[i]记录以L[i]为结尾的最大长度,dp[i]=max{dp[j]}+1,其中L[j]<=L[i]。问题就在于,找到满足<=L[i]的最大值。这时候我们可以将L[i],离散化用线段树维护区间最大值。对于,dp[i]我们查询区间[1,L[i]]的最大值更新dp[i],然后将位置L[i]的最大值更新为dp[i],后面循环这一操作就能得到答案。
而这个问题还多了一维,还要处理R[i]的递增关系,那么线段树里维护不仅仅是最大值dp[i],而是需要维护(R[i],dp[i])这样的值对。对于更新dp[i]的时候,我们首先还是要先找[1,L[i]]所对应的区间节点,然后在这些节点里找到满足R[j]<=R[i]最大的dp[j]值。按照处理L的方法,我们应该用一棵平衡树来维护(R[i], dp[i])这样的键值对,来达到快速查找R[j]<=R[i]的目的。所以线段树区间里维护的不是最大值了,而是一棵平衡树,而平衡树维护的是(R[i],dp[i])键值对。
所以查找最大值就是,先找到线段树里[1,L[i]]的所有覆盖区间,然后对于每一个区间在平衡树里查找<=R[i]的最大值。复杂度是logn*logn。
总体时间复杂度O(n*logn*logn)。更新的时候,单点更新,每个点要经过logn个区间,也就是插入logn棵平衡树,所以空间复杂度是O(nlogn),平衡树开一个20*n的内存池存储。
这题要求字典序最小,可以用逆序处理。从左到右处理dp[i]得到的是以第i个为结尾的最长长度,我们想要的是开头尽可能小,那么从右到左扫就是得到以i为开头的最大长度。
平衡树之前没写过,照着模板学着敲SBT的,代码写的很冗长就不贴了。
还有这里用树状数组求最大值会比线段树快很多,1300ms和2200ms。。。
二、cdq分治
这个是看了博客 http://blog.csdn.net/u013007900/article/details/47111319 学的。模仿着写了一遍。复杂度是T(n)=T(n/2)+O(nlogn),所以是O(nlogn*logn)。其中可以优化的地方就是,每一层分治的时候所需要的排序,可以通过预先归并排序,并记录归并中每一层的排序结果来优化。用归并优化后时间从1100ms->600ms。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cctype> #include <string> #include <vector> #include <map> #include <set> #include <vector> #include <queue> #include <algorithm> using namespace std; const int maxn=50010; typedef pair<int, int> Pr; int n,m; int a[maxn],b[maxn]; int dp[maxn], pre[maxn]; struct P { int x,y; int id; P(){} P(int xx, int yy, int dd):x(xx),y(yy), id(dd){} bool operator <(const P & a) const { return x!=a.x?x>a.x:id<a.id; } }; P arr[maxn]; P sorted[20][maxn];//记录每一层的归并排序结果 P c[maxn]; Pr bit[maxn]; //归并排序 void merge_sort(int l, int r, int dep) { if(l==r){ sorted[dep][l]=arr[l]; return; } int m=(l+r)>>1; merge_sort(l, m, dep+1); merge_sort(m+1, r, dep+1); merge(sorted[dep+1]+l, sorted[dep+1]+m+1, sorted[dep+1]+m+1, sorted[dep+1]+r+1, sorted[dep]+l); } void init(int n) { for(int i=1; i<=n; i++) bit[i]=Pr(0,0); } void update(int x, Pr v) { while(x<=n){ bit[x]=max(bit[x], v); x+=(x&-x); } } void clr(int x) //用于清空树状数组的update操作,这样就不用每次都把整个数组初始化一遍。 { while(x<=n){ bit[x]=Pr(0,0); x+=(x&-x); } } Pr query(int x) { Pr ret(0,0); while(x){ ret=max(bit[x], ret); x-=(x&-x); } return ret; } //cdq分治 void solve(int l, int r, int dep) { if(l==r){ dp[arr[l].id]=max(dp[arr[l].id],1); return; } int m=(l+r)>>1; solve(m+1, r,dep+1); for(int i=l; i<=m; i++) c[i]=arr[i]; for(int i=l; i<m+1; i++) arr[i]=sorted[dep+1][i]; //排序,直接从归并结果里面提取 int ptr=r; for(int i=m; i>=l; i--){ while(ptr>m && arr[ptr].x<=arr[i].x){ update(arr[ptr].y, Pr(dp[arr[ptr].id], -arr[ptr].id)); ptr--; } int cur=arr[i].id; Pr maxi=query(arr[i].y); dp[cur]=max(dp[pre[cur]]+1, dp[cur]); Pr tmp(dp[pre[cur]], -pre[cur]); if(maxi>tmp){ dp[cur]=maxi.first+1; pre[cur]=-maxi.second; } } for(int i=r; i>ptr; i--) //清空树状数组 clr(arr[i].y); for(int i=l; i<=m; i++) arr[i]=c[i]; solve(l, m, dep+1); for(int i=l; i<=r; i++) //合并排序 arr[i]=sorted[dep][i]; } int main() { while(cin>>n){ for(int i=1; i<=n; i++){ scanf("%d", a+i); } for(int i=1; i<=n; i++){ scanf("%d", b+i); arr[i]=P(a[i], -b[i], i); //第二维取相反数,求两个都递减的最大长度。 b[i]=-b[i]; } sort(b+1, b+1+n); for(int i=1; i<=n; i++){ arr[i].y=lower_bound(b+1,b+1+n,arr[i].y)-b; //离散化用于树状数组求最大值 } memset(dp, 0, sizeof(dp)); memset(pre, 0, sizeof(pre)); init(n); merge_sort(1, n, 0); solve(1, n, 0); int ind=0, ans=0;; for(int i=1; i<=n; i++){ if(dp[i]>ans){ ans=dp[i]; ind=i; } } printf("%d\n", ans); while(pre[ind]!=0){ printf("%d ", ind); ind=pre[ind]; } printf("%d\n", ind); } return 0; }
相关文章推荐
- [BZOJ3196][TYVJ3196][树套数][区间第k大]二逼平衡树
- 算法与数据结构八日谈之六——数据结构专题(uncompleted)
- cc150:判断一棵树是否为平衡树
- 1752. [BOI2007]摩基亚Mokia (cdq分治模板题)
- hdu 5126 stars(三维空间cdq分治)
- hdu 4699 Editor 伸展树 treap复习
- 数据结构系列之平衡树(DSW法构建)
- BZOJ 1604: [Usaco2008 Open]Cow Neighborhoods 奶牛的邻居
- 【数据结构】伸展树 Splay
- 有bug的AVL平衡树
- AVL平衡树
- 【分治】 BZOJ 1176 [Balkan2007]Mokia
- 树的周边(一)树的概念
- 树套树专题——bzoj 3110: [Zjoi2013] K大数查询 & 3236 [Ahoi2013] 作业 题解
- BZOJ 1208: [HNOI2004]宠物收养所
- BZOJ 1251: 序列终结者
- BZOJ 1588: [HNOI2002]营业额统计
- BZOJ 1861: [Zjoi2006]Book 书架
- BZOJ 3224: Tyvj 1728 普通平衡树
- POJ 2761(求区间第k小值)