Maximと増加するサブシーケンス\ operatorname {Maxim \ and \ Increasing \ Subsequence} M a x i m a n d I n c r e a s i n g S u b s e q u e n c e
タイトルリンク:luogu CF261D\ operatorname {luogu \ CF261D}l u o g u C F 2 6 1 D
トピックの翻訳
nnの長さを与えますnのBBB配列、AAAはBBを意味しますBアレイコピーttアレイは t回後に端と端が接続され、AAを見つける最も長いシーケンスは上昇している(株)kグループクエリmaxb maxbm a x bはBBを意味しますB配列の最大数
入力例
3 3 5 2
3 2 1
1 2 3
2 3 1
出力例
2
3
3
データ範囲
(下の写真を自分で見てください)
アイデア
この質問は、ツリー状の配列質問です。
ループの数が配列内の異なる数の数以上である場合、答えは配列内の異なる数の数であることがわかります。
(小から大まで、各サイクルに1つ選択してください)
次に、ループの数が配列内の異なる数の数よりも少ない状況を調べます。
もう一度範囲を見ると、n ∗ maxb <= 2 ∗ 1 0 7 n * maxb <= 2 * 10 ^ 7ん∗m a x b<=2∗1 07の場合、シーケンス全体の長さが<= 2 ∗ 1 0 7 <= 2 * 10 ^ 7でなければならないことがわかります<=2∗1 07。
(ループの数は配列内の異なる数の数より少ないため)
次に、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;
}