题意翻译
给你一个长度为n的B数组,A表示B数组复制t遍后首尾相连后的数组,求A的最长上升子序列 有k组询问 maxb表示B数组中最大的数
思路:
首先我们可以得到:当
t
t
t大于当前数列不同数字的个数时,答案就是不同数字的个数。
因为它是严格单调递增,所以在每个周期里选一个数就可以组成一个上升序列
接下来处理上述不成立的情况
我们可以设一个
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示当前周期的最长序列长度,由于数据太大,所以要开滚动数组。当枚举到当前周期时,我们考虑怎样转移。
我们想想最长上升子序列的动态转移方程:
f
[
i
]
=
m
a
x
(
f
[
j
]
)
+
1
(
j
<
i
,
a
[
j
]
<
a
[
i
]
)
f[i]=max(f[j])+1 (j<i,a[j]<a[i])
f[i]=max(f[j])+1(j<i,a[j]<a[i])。其实这道题也是一样的,我们要找到比
a
[
j
]
a[j]
a[j]小的数,也就是
a
[
j
]
−
1
a[j]-1
a[j]−1,我们就可以想到可以用树状数组维护。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<cstring>
using namespace std;
int k, n, maxb, t, sum;
int a[100100], b[20000100];
int f[100100], tree[100100];
int VL_lowbit(int x){return x & -x;}
void VL_change(int x, int y)
{
for(; x<=maxb; x+=VL_lowbit(x))
tree[x]=max(tree[x], y);
}
int VL_find(int x)
{
int ans=0;
for(; x; x-=VL_lowbit(x))
ans=max(ans, tree[x]);
return ans;
}
int main(){
scanf("%d%d%d%d", &k, &n, &maxb, &t);
while(k--)
{
sum=0;
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
if(b[a[i]]!=k+1)
sum++;
b[a[i]]=k+1;
}
if(t>=sum)
{
printf("%d\n", sum);
continue;
}
int ans=0;
for(int i=1; i<=100007; i++)
f[i]=tree[i]=0;
for(int i=1; i<=t; i++)
for(int j=1; j<=n; j++)
{
int x=VL_find(a[j]-1)+1;
if(x>f[j])
{
f[j]=x;
ans=max(ans, x);
VL_change(a[j], x);
}
if(ans>=sum)break;
}
printf("%d\n", ans);
}
return 0;
}