首页 > 代码库 > 【BZOJ 1046】 1046: [HAOI2007]上升序列
【BZOJ 1046】 1046: [HAOI2007]上升序列
1046: [HAOI2007]上升序列
Description
对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax
2 < … < axm)。那么就称P为S的一个上升序列。如果有多个P满足条件,那么我们想求字典序最小的那个。任务给
出S序列,给出若干询问。对于第i个询问,求出长度为Li的上升序列,如有多个,求出字典序最小的那个(即首先
x1最小,如果不唯一,再看x2最小……),如果不存在长度为Li的上升序列,则打印Impossible.Input
第一行一个N,表示序列一共有N个元素第二行N个数,为a1,a2,…,an 第三行一个M,表示询问次数。下面接M
行每行一个数L,表示要询问长度为L的上升序列。N<=10000,M<=1000Output
对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.
Sample Input
6
3 4 1 2 3 6
3
6
4
5Sample Output
Impossible
1 2 3 6
ImpossibleHINT
Source
【分析】
好坑啊,字典序最小指的是下标字典序。。。
那么,我们首先求出以每个数为开头上升序列长度,就是倒着做最长下降子序列。
然后,遇到第一个f[i]>=x的,就输出,然后更新条件,继续在后面找,时间是每次询问是线性的。
==个人觉得数值字典序最小更难吧。。。我打了一个数值字典序最小的,不知道对不对。。
LIS做法:
d[i]表示长度为i的IS中,最后一个数字最小的是什么(因为最后一个数字最小最有利于后面的)
显然d数组是单调递增的。显然我们每次插一个数进去只会改变一个d值或者让LIS增加一位。
若a[i]>a[d[mx]] ,那么可以吧a[i]放在目前的LIS后面,那么mx+1。
否则,二分查找一个x,使得x满足a[i]>d[x]&&a[i]<=d[x+1],那么可以把a[i]放在长度为x的IS后面,替换长度为x+1的IS。
下降序列大于小于号反一下就好了。
本题code:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Maxn 10010 8 #define INF 0xfffffff 9 10 int a[Maxn],b[Maxn],f[Maxn]; 11 int lt[Maxn]; 12 13 int mymax(int x,int y) {return x>y?x:y;} 14 15 int n,mx; 16 17 int tdiv(int x,int l,int r) 18 { 19 while(l<r) 20 { 21 int mid=(l+r+1)>>1; 22 if(x<a[lt[mid]]) l=mid; 23 else r=mid-1; 24 } 25 return l; 26 } 27 28 void ffind() 29 { 30 mx=0; 31 memset(lt,0,sizeof(lt)); 32 a[0]=INF; 33 for(int i=n;i>=1;i--) 34 { 35 if(mx==0||a[i]<a[lt[mx]]) 36 { 37 mx++; 38 lt[mx]=i; 39 f[i]=mx; 40 } 41 else 42 { 43 int x=tdiv(a[i],0,mx); 44 f[i]=x+1; 45 lt[x+1]=i; 46 } 47 } 48 } 49 50 void output(int x) 51 { 52 int now=-INF; 53 for(int i=1;i<=n;i++) 54 { 55 if(f[i]>=x&&a[i]>now) 56 { 57 if(now==-INF) printf("%d",a[i]); 58 else printf(" %d",a[i]); 59 now=a[i]; 60 x--; 61 } 62 if(x==0) break; 63 } 64 } 65 66 int main() 67 { 68 freopen("a.in","r",stdin); 69 freopen("a.out","w",stdout); 70 scanf("%d",&n); 71 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 72 ffind(); 73 int q; 74 scanf("%d",&q); 75 while(q--) 76 { 77 int x; 78 scanf("%d",&x); 79 if(x>mx) printf("Impossible\n"); 80 else 81 { 82 output(x); 83 printf("\n"); 84 } 85 } 86 return 0; 87 }
数值最小(不知道对不对的)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 #define Maxn 10010 8 #define INF 0xfffffff 9 10 int a[Maxn],b[Maxn],f[Maxn]; 11 int lt[Maxn],nt[Maxn]; 12 13 int mymax(int x,int y) {return x>y?x:y;} 14 15 int n,mx; 16 17 int tdiv(int x,int l,int r) 18 { 19 while(l<r) 20 { 21 int mid=(l+r+1)>>1; 22 if(x>a[lt[mid]]) l=mid; 23 else r=mid-1; 24 } 25 return l; 26 } 27 28 void ffind() 29 { 30 mx=0; 31 memset(nt,0,sizeof(nt)); 32 memset(lt,0,sizeof(lt)); 33 a[0]=-INF; 34 for(int i=1;i<=n;i++) 35 { 36 if(mx==0||a[i]>a[lt[mx]]) 37 { 38 mx++; 39 lt[mx]=i; 40 nt[i]=lt[mx-1]; 41 } 42 else 43 { 44 int x=tdiv(a[i],0,mx); 45 lt[x+1]=i; 46 nt[i]=lt[x]; 47 } 48 } 49 } 50 51 void output(int x) 52 { 53 if(x==0) return; 54 output(nt[x]); 55 printf("%d ",a[x]); 56 } 57 58 int main() 59 { 60 scanf("%d",&n); 61 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 62 ffind(); 63 int q; 64 scanf("%d",&q); 65 while(q--) 66 { 67 int x; 68 scanf("%d",&x); 69 if(x>mx) printf("Impossible\n"); 70 else 71 { 72 output(lt[x]); 73 printf("\n"); 74 } 75 } 76 return 0; 77 }
2016-12-15 18:29:39
【BZOJ 1046】 1046: [HAOI2007]上升序列