Maxim and Increasing Subsequence \operatorname{Maxim\ and\ Increasing\ Subsequence} Maxim and Increasing Subsequence
题目链接: luogu CF261D \operatorname{luogu\ CF261D} luogu CF261D
题目翻译
给你一个长度为 n n n 的 B B B 数组, A A A 表示 B B B 数组复制 t t t 遍后首尾相连后的数组,求 A A A 的最长上升子序列 有 k k k 组询问 m a x b maxb maxb 表示 B B B 数组中最大的数
样例输入
3 3 5 2
3 2 1
1 2 3
2 3 1
样例输出
2
3
3
数据范围
(自己看下面的图)
思路
这道题是一道树状数组题。
我们可以发现,如果循环的次数超过或者等于数组中不同数字的个数,那答案就是数组中不同数字的个数。
(每次循环都选一个,从小到大依次选)
那我们就看循环的次数小于数组中不同数字的个数的情况。
再看范围,发现
n
∗
m
a
x
b
<
=
2
∗
1
0
7
n*maxb<=2*10^7
n∗maxb<=2∗107 ,那我们就发现总序列的长度一定
<
=
2
∗
1
0
7
<=2*10^7
<=2∗107 。
(因为循环的次数小于数组中不同数字的个数)
那就可以 dp ,找最大的最长上升子序列可以用树状数组来求。
(因为听说用线段树会 TLE )
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int k, n, maxb, t;
int a[100001], sum, ans;
int tree[100001], f[100001];
bool in[100001];
int get(int now) {//找到当前最长上升子序列
int re = 0;
for (; now; now -= now & (-now))
re = max(re, tree[now]);
return re;
}
void up(int now, int x) {//修改值
for (; now <= maxb; now += now & (-now))
tree[now] = max(tree[now], x);
}
int main() {
scanf("%d %d %d %d", &k, &n, &maxb, &t);//读入
for (int times = 1; times <= k; times++) {
sum = 0;//初始化
ans = 0;
memset(in, 0, sizeof(in));
memset(tree, 0, sizeof(tree));
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);//读入
if (!in[a[i]]) {
in[a[i]] = 1;
sum++;//记录有多少个不同的数
}
}
if (sum <= t) {//循环次数超过数的个数,直接输出数的个数
printf("%d\n", sum);
continue;
}
for (int i = 1; i <= t; i++)//枚举复制了多少次
for (int j = 1; j <= n; j++) {//枚举到哪一个
int now = get(a[j] - 1) + 1;//求出最长上升子序列
if (now > f[j]) {//比上一次复制的还要高
f[j] = now;
ans = max(ans, now);
up(a[j], now);//修改
}
}
printf("%d\n", ans);//输出
}
return 0;
}