Maxim and Increasing Subsequence

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 nmaxb<=2107 ,那我们就发现总序列的长度一定 < = 2 ∗ 1 0 7 <=2*10^7 <=2107
(因为循环的次数小于数组中不同数字的个数)

那就可以 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108116934