【BZOJ】【1046】【HAOI2007】上升序列
DP+贪心
啊……其实是个水题,想的复杂了
令f[i]表示以 i 为起始位置的最长上升子序列的长度,那么对于一个询问x,我们可以贪心地从前往后扫,如果f[i]>=x && a[i]>last,则x--,last=a[i]
保证$x_i$(下标)字典序最小……
1 /************************************************************** 2 Problem: 1046 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:2116 ms 7 Memory:1428 kb 8 ****************************************************************/ 9 10 //BZOJ 1046 11 #include<vector> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 inline int getint(){ 23 int v=0,sign=1; char ch=getchar(); 24 while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} 25 while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} 26 return v*sign; 27 } 28 const int N=1e4+10,INF=~0u>>2; 29 typedef long long LL; 30 /******************tamplate*********************/ 31 int n,m,a[N],b[N],len,f[N],ans[N]; 32 int Find(int x){ 33 int l=1,r=len,mid,ans=len+1; 34 while(l<=r){ 35 mid=l+r>>1; 36 if (b[mid]<=x) ans=mid,r=mid-1; 37 else l=mid+1; 38 } 39 return ans; 40 } 41 int main(){ 42 #ifndef ONLINE_JUDGE 43 freopen("1046.in","r",stdin); 44 freopen("1046.out","w",stdout); 45 #endif 46 n=getint(); 47 F(i,1,n) a[i]=getint(); 48 D(i,n,1){ 49 int x=Find(a[i]); 50 f[i]=x; b[x]=a[i]; 51 if (x>len) len=x; 52 } 53 m=getint();int x; 54 while(m--){ 55 x=getint(); 56 if (len<x) {puts("Impossible");continue;} 57 int last=0; 58 F(i,1,n) 59 if (f[i]>=x && a[i]>last){ 60 printf("%d",a[i]); 61 if (x!=1) printf(" "); 62 last=a[i]; 63 x--; 64 if (x==0) break; 65 } 66 puts(""); 67 } 68 return 0; 69 } 70
P.S.一开始想成数值字典序最小了……如果是数值字典序的话也可做,方法类似?(以下内容与本题解法无关)
预处理出来一张表,在这张表 f 中,f[i]里存的是最长上升子序列长度>=i 的数的下标,且满足这些数(即下标对应的数)是单调递减的。
感觉说起来好怪……贴下代码吧
就是满足下标单调增,但值单调减
1 #include<vector> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;++i) 8 #define F(i,j,n) for(int i=j;i<=n;++i) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 #define pb push_back 11 using namespace std; 12 inline int getint(){ 13 int v=0,sign=1; char ch=getchar(); 14 while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} 15 while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} 16 return v*sign; 17 } 18 const int N=1e4+10,INF=~0u>>2; 19 typedef long long LL; 20 /******************tamplate*********************/ 21 int n,a[N],best,ans[N]; 22 vector<int>f[N]; 23 vector<int>::iterator tmp; 24 int main(){ 25 #ifndef ONLINE_JUDGE 26 freopen("1046.in","r",stdin); 27 freopen("1046.out","w",stdout); 28 #endif 29 n=getint(); 30 F(i,1,n) a[i]=getint(); 31 f[1].pb(1); best=1; 32 F(i,2,n){ 33 D(j,best,2){ 34 if (a[i]<a[f[j][f[j].size()-1]] && 35 a[f[j-1][f[j-1].size()-1]]<a[i]) 36 f[j].pb(i); 37 } 38 if (a[i]>a[f[best][f[best].size()-1]]){ 39 best++; 40 f[best].pb(i); 41 } 42 if (a[i]<a[f[1][f[1].size()-1]]) f[1].pb(i); 43 } 44 int m=getint(), x; 45 while(m--){ 46 x=getint(); 47 if (f[x].empty()){puts("Impossible");continue;} 48 ans[x]=f[x][f[x].size()-1]; 49 D(i,x-1,1){ 50 tmp=lower_bound(f[i].begin(),f[i].end(),ans[x]); 51 tmp--; 52 ans[i]=*tmp; 53 } 54 F(i,1,x) printf("%d ",a[ans[i]]); 55 puts(""); 56 } 57 return 0; 58 }
也是贪心地去找最优解>_>
1046: [HAOI2007]上升序列
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2866 Solved: 960
[Submit][Status][Discuss]
Description
对 于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax2 < … < axm)。那么就称P为S的一个上升序列。如果有多个P满足条件,那么我们想求字典序最小的那个。任务给出S序列,给出若干询问。对于第i个询问,求出长 度为Li的上升序列,如有多个,求出字典序最小的那个(即首先x1最小,如果不唯一,再看x2最小……),如果不存在长度为Li的上升序列,则打印 Impossible.
Input
第一行一个N,表示序列一共有N个元素第二行N个数,为a1,a2,…,an 第三行一个M,表示询问次数。下面接M行每行一个数L,表示要询问长度为L的上升序列。
Output
对于每个询问,如果对应的序列存在,则输出,否则打印Impossible.
Sample Input
6
3 4 1 2 3 6
3
6
4
5
3 4 1 2 3 6
3
6
4
5
Sample Output
Impossible
1 2 3 6
Impossible
1 2 3 6
Impossible
HINT
数据范围
N<=10000
M<=1000