(nlogn求LIS,确定状态唯一性)LIS of Sequence CodeForces - 486E

2024-04-16 03:32

本文主要是介绍(nlogn求LIS,确定状态唯一性)LIS of Sequence CodeForces - 486E,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

The next “Data Structures and Algorithms” lesson will be about Longest Increasing Subsequence (LIS for short) of a sequence. For better understanding, Nam decided to learn it a few days before the lesson.

Nam created a sequence a consisting of n (1 ≤ n ≤ 105) elements a1, a2, …, an (1 ≤ ai ≤ 105). A subsequence ai1, ai2, …, aik where 1 ≤ i1 < i2 < … < ik ≤ n is called increasing if ai1 < ai2 < ai3 < … < aik. An increasing subsequence is called longest if it has maximum length among all increasing subsequences.

Nam realizes that a sequence may have several longest increasing subsequences. Hence, he divides all indexes i (1 ≤ i ≤ n), into three groups:

group of all i such that ai belongs to no longest increasing subsequences.
group of all i such that ai belongs to at least one but not every longest increasing subsequence.
group of all i such that ai belongs to every longest increasing subsequence.
Since the number of longest increasing subsequences of a may be very large, categorizing process is very difficult. Your task is to help him finish this job.

Input
The first line contains the single integer n (1 ≤ n ≤ 105) denoting the number of elements of sequence a.

The second line contains n space-separated integers a1, a2, …, an (1 ≤ ai ≤ 105).

Output
Print a string consisting of n characters. i-th character should be ‘1’, ‘2’ or ‘3’ depending on which group among listed above index i belongs to.

Examples
Input
1
4
Output
3
Input
4
1 3 2 5
Output
3223
Input
4
1 5 2 3
Output
3133
Note
In the second sample, sequence a consists of 4 elements: {a1, a2, a3, a4} = {1, 3, 2, 5}. Sequence a has exactly 2 longest increasing subsequences of length 3, they are {a1, a2, a4} = {1, 3, 5} and {a1, a3, a4} = {1, 2, 5}.

In the third sample, sequence a consists of 4 elements: {a1, a2, a3, a4} = {1, 5, 2, 3}. Sequence a have exactly 1 longest increasing subsequence of length 3, that is {a1, a3, a4} = {1, 2, 3}.

题意:
n个数,每一个数有三种状态:
(1)不位于任何LIS序列上
(2)位于LIS序列上但是不是所有LIS序列上(LIS序列可能很多,如1 3 2 5,其中2在LIS序列1 2 5上,但是1 3 5也是一个LIS序列)
(3)位于唯一LIS序列上(如1 3 2 5中的5和1)
输出每个数表示的状态
思路:
(1)题目数据开到了1e5,首先求LIS的过程就不能直接n^2转移。有两种nlogn求LIS的方法,一种是 重 新 建 模 \color{red}{重新建模}

  • 原有的dp数组定义是dp[i],i代表a[i]结尾的LIS长度。这样的转移方程是dp[i] = max(dp[j] + 1)(i > j && a[i] > a[j])。
  • 新的定义是dp[i]:长度为i的最长上升子序列元素末尾的最小值。例如 1 3 2 5,1 3 和 1 2都能到达5的状态达到3的长度,但是2比3相对更优。在序列长度相同的前提下, 肯 定 是 \color{red}{肯定是} 结尾数字越小,未来能够匹配的相对更多。该过程可以用二分优化,每次将a[i]更新到能够更新的最后位置。复杂度为nlogn
  • 对于原有模型,麻烦的主要在于那个小于a[i]并且lis值最大的子状态的找寻。学习树状数组写逆序对的时候,为了找寻i > j && a[i] < a[j]的数的个数,我们是将a[i]当做下标,依次更新。同样的,我们需要找寻i > j && a[i] > a[j] && dp[j]最大的j,我们或许也可以通过树状数组求解
  • 我们要满足i > j的条件,直接按顺序for过来就可以了。要满足a[i] > a[j]的条件,类似逆序对的做法,以a[i]/a[j]为下标,下标小于a[i]的所有下标数,都是可选的a[j]。要找寻max{dp[j]},我们则可以将树状数组改为求max的过程,而每一个下标存的是到该位置的最长上升子序列长度。这样寻找子状态过程的复杂度就优化为了nlogn。
    比较好的博客:树状数组求LIS[https://www.cnblogs.com/Rosebud/p/9845935.html]

(2)除了求LIS的过程,题目还需要求是否位于LIS上。但是在每一个位置只能得到以位置结尾的LIS。可行的办法是维护两个dp数组,dp1[i]记录i的LIS,dp2[i]记录n到i的最长下降子序列。只要dp1[i] + dp2[i] = LIS + 1(加1是因为a[i]在两个dp数组中都出现了),就说明i在LIS上。但是即使思路得出,计算dp2[i]也是需要技巧的。

  • 我们需要计算n到i的最长下降子序列 -->计算i到n的最长上升子序列 --> 计算原序列翻转后1到i的最长下降子序列(此时dp状态也翻转了) --> 计算现在这个序列大小关系反转后1到i的最长上升子序列。(可以加个负号,但由于是作为下标,不能成为负数,那么就得用一个很大的数和每个数做差),那么由此方法得出的dp数组为dp[i]:1到i的的最长上升子序列,翻转一下就成了计算n到i的最长下降子序列。

(3)题目还要判断是否在唯一的LIS上。在**(2)**中我们已经能够判断出i是否在LIS上了,还是考虑样例1 3 2 5,很明显1和5在唯一的LIS上,而3和2不在唯一的LIS上,一个明显的特点是dp1[i] + dp2[i] - 1 = LIS的情况中,3和2的dp1都为2,或许这里是突破口。由此考虑状态的定义:LIS = max{dp1[k]} = dp1[i] + dp2[i] - 1;其中一定有k >= i,那么dp[k]一定是由dp[i]转移过来的,假设dp[i]不唯一,转移的过程一定不唯一,那么由i转移得到的LIS也一定不唯一,再由此记录一下所有满足在LIS上的dp1[i]判一下重就可以了。

总结: 写的有点啰嗦了,不过还算是把自己解整道题的学习过程给写出来了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>using namespace std;
const int maxn = 1e5+7;
int c[maxn];
int a[maxn];
int dp1[maxn],dp2[maxn];
int res[maxn];
map<int,int> mp;
void add(int i,int val)
{while(i < maxn){c[i] = max(c[i],val);i += (i & (-i));}
}int query(int i)
{int ans = 0;while(i){ans = max(ans,c[i]);i -= (i & (-i));}return ans;
}int main()
{int n;scanf("%d",&n);int LIS = 0;for(int i = 1;i <= n;i++){scanf("%d",&a[i]);dp1[i] = 1 + query(a[i] - 1);add(a[i],dp1[i]);LIS = max(LIS,dp1[i]);}reverse(a + 1,a + 1 + n);memset(c,0,sizeof(c));for(int i = 1;i <= n;i++){a[i] = maxn - a[i] + 1;dp2[i] = 1 + query(a[i] - 1);add(a[i],dp2[i]);dp2[i] = query(a[i] - 1) + 1;}reverse(dp2 + 1,dp2 + 1 + n);for(int i = 1;i <= n;i++){if(dp1[i] + dp2[i] - 1 == LIS){mp[dp1[i]]++;}elseres[i] = 1;}for(int i = 1;i <= n;i++){if(dp1[i] + dp2[i] - 1 == LIS){if(mp[dp1[i]] == 1)res[i] = 3;elseres[i] = 2;}printf("%d",res[i]);}return 0;
}

这篇关于(nlogn求LIS,确定状态唯一性)LIS of Sequence CodeForces - 486E的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/907689

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

Codeforces Beta Round #47 C凸包 (最终写法)

题意慢慢看。 typedef long long LL ;int cmp(double x){if(fabs(x) < 1e-8) return 0 ;return x > 0 ? 1 : -1 ;}struct point{double x , y ;point(){}point(double _x , double _y):x(_x) , y(_y){}point op

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

hdu3006状态dp

给你n个集合。集合中均为数字且数字的范围在[1,m]内。m<=14。现在问用这些集合能组成多少个集合自己本身也算。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.Inp

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

实例:如何统计当前主机的连接状态和连接数

统计当前主机的连接状态和连接数 在 Linux 中,可使用 ss 命令来查看主机的网络连接状态。以下是统计当前主机连接状态和连接主机数量的具体操作。 1. 统计当前主机的连接状态 使用 ss 命令结合 grep、cut、sort 和 uniq 命令来统计当前主机的 TCP 连接状态。 ss -nta | grep -v '^State' | cut -d " " -f 1 | sort |