>Description
给你一个长度为n的B数组,A表示B数组复制t遍后首尾相连后的数组,求A的最长上升子序列 有k组询问 maxb表示B数组中最大的数
(1<=k<=10; 1<=n,maxb<=10^5; 1<=t<=10^9; n * maxb<=2*(10^7))
>解题思路
设 s u m sum sum为B数组中不同数字的个数
可以发现:当 t t t大于等于 s u m sum sum时,ans一定为sum,因为我们可以在每一个A数组中的B数组中只选择一个不同数,最后组成一个长度为sum的最长上升序列(神奇)
所以我们只用考虑
t
t
t小于
s
u
m
sum
sum的情况,因为sum≤maxb,所以t肯定也≤maxb,这时
n
∗
t
≤
2
∗
1
0
7
n*t≤2*10^7
n∗t≤2∗107,所以A数组不会爆掉
这时候我们只需要维护一个A数组的树状数组(用来求i范围中的ans)与DP(f[i]表示(对于B数组)1~i中的ans)
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100005
using namespace std;
int k, n, maxb, t, sum, b[N], a[N], f[N], ans;
bool c[N];
void add (int x, int y)
{
for (; x <= maxb; x += x & (-x)) //A数组中最大的数也同是maxb
a[x] = max (a[x], y); //ans是取最长上升子序列
} //树状数组修改
int ask (int x)
{
int l = 0;
for (; x; x -= x & (-x))
l = max (l, a[x]); //取最大
return l;
} //树状数组询问
void work ()
{
memset (c, 0, sizeof (c));
memset (f, 0, sizeof (f));
memset (a, 0, sizeof (a));
sum = ans = 0;
for (int i = 1; i <= n; i++)
{
scanf ("%d", &b[i]);
if (!c[b[i]]) c[b[i]] = 1, sum++; //记录不同的个数
}
if (t >= sum)
{
printf ("%d\n", sum);
return;
}
for (int i = 1; i <= t; i++)
for (int j = 1; j <= n; j++) //暴力模拟A数组
{
int s = ask (b[j] - 1) + 1;
if (s > f[j])
{
f[j] = s;
add (b[j], s); //需要更新才更新
ans = max (ans, s);
}
if (ans >= sum) break; //ans最大只能为sum,小优化
}
printf ("%d\n", ans);
}
int main()
{
scanf("%d%d%d%d", &k, &n, &maxb, &t);
for (int i = 1; i <= k; i++)
work ();
return 0;
}