2018浙江省賽 E題 ZOJ4028 LIS
阿新 • • 發佈:2018-12-31
Sample Input
4 6 1 2 3 2 4 3 0 5 2 4 3 3 1 2 3 5 1 5 5 1 2 1 3 1 100 200 200 300 200 400 400 500 100 500 7 1 2 3 1 1 4 2 0 3 0 3 0 3 0 3 0 3 0 3 0 3 2 1 1 1 2 2 3
Sample Output
1 2 3 2 5 3 200 300 200 500 200 0 1 2 0 0 3 1 2 2
【題意】給定一個數組f[],f[i]表示以第i個字元為終點的最長上升子序列長度,然後輸入n行,每行有一個區間[l,r],表示第i個數字的取值範圍,輸出符合條件的任意一組序列,保證有解。
【思路】對於給定的f[]按照大小排序,大小相等按照下標從小到大排序,ans[]儲存最後要輸出的答案。很明顯,對於f[i]越小且越靠前的值,應該給ans[i]的值應大於下標i小且距離i最近的長度為f[i]-1的元素,並且在區間[l,r]能取的最小值;同時,對於最長上升子序列長度相等的元素來說,下標大的元素的值應該小於等於下標小的值。
【程式碼如下】
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; struct node{ int f,l,r,indx; }p[N]; vector<int>vct[N]; int t,n,ans[N],a[N]; bool cmp(node A, node B){ if(A.f == B.f) return A.indx < B.indx; return A.f < B.f; } int main(){ scanf("%d",&t); while(t --){ scanf("%d",&n); for(int i = 0; i <= n+1; i ++) vct[i].clear(); for(int i = 1; i <= n; i ++) scanf("%d",&p[i].f),p[i].indx = i; for(int i = 1; i <= n; i ++) scanf("%d%d",&p[i].l,&p[i].r),a[i]=p[i].r; sort(p+1,p+n+1,cmp); for(int i = 1; i <= n; i ++){ int len = p[i].f; int l = p[i].l,r=p[i].r; int t=-1; if(len==1){ ans[p[i].indx] = l; int lena = vct[len].size(); for(int j = lena-1; j >= 0; j --){ int tt = vct[len][j]; if(l > ans[tt]) ans[tt]=l; else break; } } else{ int in = lower_bound(vct[len-1].begin(),vct[len-1].end(),p[i].indx) - vct[len-1].begin()-1; int tt = vct[len-1][in]; if(ans[tt] >= l && ans[tt] < r) t = ans[tt]+1; else if(ans[tt] < l) t = l; int lena = vct[len].size(); for(int j = lena-1; j >= 0; j --){ int x = vct[len][j]; if(ans[x] < t){ ans[x] = min(t,a[x]); } else break; } } if(t>0) ans[p[i].indx] = t; vct[len].push_back(p[i].indx); } for(int i = 1; i <= n; i ++){ if(i>1) printf(" "); printf("%d",ans[i]); } printf("\n"); } return 0; }