CF1304D·Shortest and Longest LIS

初见安~昨晚的CF打得真的是……兴奋过头了。这里是传送门:Codeforces #620 D Shortest ans Longest LIS(Div2

CF1304D·Shortest and Longest LIS_第1张图片题解

题意:给出n个数之间的大小关系,构造两个排列,让其最长/最短上升子序列最长/短

构造题哈。第一反应是转化一下,比如我们可以利用大小关系转化成一个长度为n的01序列。第i位为1表示a[i]>a[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
#include
#include
#include
#include
#include
#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——

你可能感兴趣的:(Codeforces)