LIS
Time Limit: 1 Second Memory Limit: 65536 KB Special Judge
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
【题目链接】http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4028
【题意】给定一个数组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;
}