【CodeForces 261D】Maxim and Increasing Subsequence
链接:
题目大意:
将长度为 \(n\) 的 \(b\) 数组复制 \(t\),首尾相连后得到 \(a\) 数组,求 \(a\) 的最长上升序列长度。
正文:
如果 \(t\) 足够大,答案是固定的。设 \(cnt\) 表示 \(b\) 数组不同数字个数,则显然有当 \(cnt\leq t\) 时答案是 \(cnt\)。
再考虑 \(cnt>t\) 的情况。跑一趟 \(O(n^2t^2)\) 的最长上升子序列显然不可能,考虑优化它。设 \(f_i\) 表示 \(a\) 数组中第 \(i\) 个数在最长上升序列的最长长度。则有:
\[f_i=\max_{j<i,a_j<a_i}\{f_i+1\}
\]
其中 \(\max\) 可以用树状数组维护。
还有一点,空间 \(O(nt)\) 是接受不了的,考虑滚动数组,滚到 \(O(n)\) 就行了。
代码:
int k, t, n, maxb, cnt;
bool a[N];
int tr[N], f[N], b[N];
void update(int x, int val)
{
for (; x <= maxb; x += x & -x) tr[x] = max(tr[x], val);
return;
}
int query(int x)
{
int ans = 0;
for (; x; x -= x & -x) ans = max(ans, tr[x]);
return ans;
}
int ans;
int main()
{
for (scanf("%d%d%d%d", &k, &n, &maxb, &t); k--; )
{
ans = 0;
memset (a, 0, sizeof a);
memset (b, 0, sizeof b);
memset (tr, 0, sizeof tr);
memset (f, 0, sizeof f);
cnt = 0;
for (int i = 1; i <= n; i++)
{
scanf ("%d", &b[i]);
if (!a[b[i]]) cnt++, a[b[i]] = 1;
}
if (cnt <= t) {printf ("%d\n", cnt); continue;}
for (int j = 1; j <= t; j++)
for (int i = 1; i <= n; i++)
{
int tmp = query(b[i] - 1) + 1;
if (tmp > f[i])
{
f[i] = tmp;
update(b[i], tmp);
ans = max (ans, tmp);
}
}
printf ("%d\n", ans);
}
return 0;
}