初见安~昨晚的CF打得真的是……兴奋过头了。这里是传送门:Codeforces #620 D Shortest ans Longest LIS(Div2
题解
题意:给出n个数之间的大小关系,构造两个排列,让其最长/最短上升子序列最长/短。
构造题哈。第一反应是转化一下,比如我们可以利用大小关系转化成一个长度为n的01序列。第i位为1表示,反之小于。第一位我们默认为1。那么为了便于构造,我们肯定是尽量卡边缘,换言之,每一段连续的0和连续的1代表的数就是连续的。就比如序列1111,我们就可以放1234,序列0000,我们就放4321。所以,如果是构造最长,我们就让所有的1可以连起来。如果是最短,就不让每一段1连起来就可以了。
先考虑最长。因为不考虑0位置,所以我们可以直接让所有的0都不产生贡献,也就是从右往左【逆向】在0的位置放1,2,3,4……。考虑1的位置,为了让所有1连起来,直接接着从左往右填就可以了。比如01序列是1001111,那么我们就可以构造出3214567。
再看最短。每一段不连起来,那每一段1也倒过来放就行了嘛。1001111就可以构造出7213456。
正确性比较显然。【迎评。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 200005
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int T, n, a[maxn], ans[maxn], col[maxn];
char s[maxn];
signed main() {
T = read();
while(T--) {
memset(col, 0, sizeof col);
n = read(), scanf("%s", s + 2);
a[1] = 1;
register int cnt0 = 0, cnt1 = 0;
for(int i = 2; i <= n; i++) if(s[i] == '<') a[i] = 1, cnt1++; else a[i] = 0, cnt0++;
for(int i = 1; i <= n; i++) if(a[i]) {
register int cnt = 0; while(a[i] && i <= n) cnt++, i++;
i--; col[i] = cnt;//col[i]表示以i结尾的这一段1的长度
}
register int now = 1;//0的位置两种都一样
for(int i = n; i > 0; i--) if(!a[i]) ans[i] = now, now++;
now = n;//最短的情况
for(int i = 1; i <= n; i++) if(col[i]) {
for(int j = i; j >= i - col[i] + 1; j--) ans[j] = now, now--;
}
for(int i = 1; i <= n; i++) printf("%d ", ans[i]); puts("");
now = cnt0 + 1;//最长的情况
for(int i = 1; i <= n; i++) if(a[i]) ans[i] = now, now++;
for(int i = 1; i <= n; i++) printf("%d ", ans[i]); puts("");
}
return 0;
}
后面有可能会写E。
迎评:)
——End——