2020 算法上机赛 C2 - H Paint
题面
Kazamori 得到了一个长度为 \(n\) 的整数序列 \(a_1,a_2,...,a_n\) 。对这 \(n\) 个数中的每一个数,他会选择一种颜色,然后把那个数涂上那种颜色。涂色的要求是:
\[如果 a_i 和 a_j (i<j) 被涂上了同一种颜色,那么 a_i<a_j 。
\]
Kazamori 想知道,如果要满足要求,最少需要多少种颜色。
输入
多组输入数据
每组数据第一行一个整数 \(n\) ,第二行 \(n\) 个用空格隔开的整数 \(a_i\)
输出
对于每组数据,输出一行,一个整数,表示最少需要的颜色种数。
输入样例
5
2 1 4 5 3
输出样例
2
数据范围
\(1≤n≤10^5\)
\(0≤ai≤10^9\)
做法
感兴趣的可以去看一下:最小链覆盖 - Dilworth定理
结论是:正串的最少链覆盖,等于反串的最长链长度。
不过这个题最难的是考场上只允许 C 的提交,需要手写一个 \(O(n\log n)\) 的最长上升子序列
(如果可以用 c++ 的话因该会过不少人吧)
#include<stdio.h>
#include<stdlib.h>
int h[100005], H[100005], dp[100005];
int LIS(int n)
{
int ans = 1, l, r, mid;
for(int i = 1;i <= n; i++){
l = 1; r = ans;
mid = (l + r)>>1;
while(l < r){
if(h[i] < dp[mid]) r = mid;
else l = mid+1;
mid = (l + r) >> 1;
}
dp[l] = h[i];
if(l == ans) ans++;
}
return ans - 1;
}
int n;
void work() {
for(int i = 1;i <= n; ++i) scanf("%d", &H[i]);
for(int i = n;i >= 1; --i) h[i] = H[n - i + 1];
printf("%d\n", LIS(n));
}
int main()
{
while(scanf("%d", &n) != EOF)
work();
return 0;
}