写在最前面的话:如果你懂这道题的线段树或者树状数组解法,那么本题解对你可能没有帮助。
[ABC354F] Useless for LIS
题面翻译
- 给定一个长度为 \(n\) 的序列 \(a\)。求出所有 \(i\) 使得存在任意一个 \(a\) 的最长上升子序列包含 \(i\)。多测。
- \(1 \le T, n, \sum n \le 2 \times 10^5\),\(1 \le a_i \le 10^9\)。
题目描述
長さ $ N $ の整数列 $ A $ が与えられます。
$ t\ =\ 1,\ 2,\ \dots\ ,N $ について、 $ A_t $ が $ A $ の最長増加部分列に含まれることがあるか判定してください。
$ A_t $ が $ A $ の最長増加部分列に含まれることがあるとは、以下のことをいいます。
-
最長増加部分列の長さを $ L $ とする。各要素が $ 1 $ 以上 $ N $ 以下の単調増加な整数列 $ i\ =\ (i_1,\ i_2,\ \dots\ ,i_L)\ (i_1\ であって以下をすべて満たすものが存在する。 $
- $ A_{i_1} $
- ある $ k\ (1\ \leq\ k\ \leq\ L) $ が存在して $ i_k\ =\ t $ である
$ T $ 個のテストケースが与えられるので、それぞれについて答えを求めてください。
最長増加部分列とは?列 $ A $ の部分列とは $ A $ の要素をいくつか抜き出して元の順に並べてできる列を指します。
列 $ A $ の最長増加部分列とは、 $ A $ の狭義単調増加な部分列のうち列の長さが最大のものを指します。
输入格式
入力は以下の形式で標準入力から与えられる。
$ T $ $ \mathrm{case}_1 $ $ \mathrm{case}_2 $ $ \vdots $ $ \mathrm{case}_T $
ここで $ \mathrm{case_i} $ は $ i $ 番目のケースの入力を意味する。各ケースは以下の形式で与えられる。
$ N $ $ A_1 $ $ A_2 $ $ \cdots $ $ A_N $
输出格式
以下の形式で出力せよ。
\(\mathrm{answer}_1\) \(\mathrm{answer}_2\) \(\vdots\) \(\mathrm{answer}_T\)
ここで $ \mathrm{answer}_i $ は $ i $ 番目のケースの出力を意味する。各ケースについては、次の通りである。
$ A_t $ が $ A $ の最長増加部分列に含まれることがある $ t $ が $ m $ 個存在し、昇順に $ i_1,\ i_2,\ \dots\ ,i_m $ であったとする。このとき、以下の形式で出力せよ。
$ m $ $ i_1 $ $ i_2 $ $ \cdots $ $ i_m $
样例 #1
样例输入 #1
1
5
2 1 4 5 3
样例输出 #1
4
1 2 3 4
样例 #2
样例输入 #2
2
6
2 5 3 4 3 4
5
10000 1000 100 1 10
样例输出 #2
5
1 3 4 5 6
2
4 5
提示
制約
- \(1\ \leq\ T\ \leq\ 2\ \times\ 10^5\)
- \(1\ \leq\ N\ \leq\ 2\ \times\ 10^5\)
- \(1\ \leq\ A_i\ \leq\ 10^9\)
- 全てのテストケースにおける \(N\) の総和は \(2\ \times\ 10^5\) 以下
Sample Explanation 1
最長増加部分列の $ 1 $ つは $ (2,\ 4,\ 5) $ で、長さは $ 3 $ です。$ (1,\ 4,\ 5) $ も最長増加部分列の $ 1 $ つです。しかし、 $ A_5 $ を含む最長増加部分列はありません。 よって、 $ 1,\ 2,\ 3,\ 4 $ を出力します。
题目解析
给你一个长度为 \(N\) 的序列,问你那些元素有可能出现在最长上升子序列中。\(N \leq 2 \times 10^{5}\)。
首先,一个个枚举最长上生子序列然后记录肯定不行(可以用搜索做,但 \(N\) 很大,试问 \(2^{2 \times 10^{5}}\) 会不会TLE),而且第一反应应该是动态规划,而且是二分优化的动态规划(\(N \log N\) 不会超)。可以枚举每个 \(a_i\),然后看看 \(a_i\) 能不能和其他元素一起构成一个长度等于原序列最长上生子序列长度的子序列,这个包含 \(a_i\) 的自序列就可以拆成两部分算:\(a_i\) 和小于 \(a_i\)的,\(a_i\) 和大于 \(a_i\) 的(\(a_i\) 算了两次,算的时候要减一),也就是以 \(a_i\) 结尾的和以 \(a_i\) 开头的。把它们的长度加起来减去一,如果等于原序列最长上生子序列的长度,\(i\) 就是答案之一。
以 \(a_i\) 结尾的最长上升自序列的长度好算,那么以 \(a_i\) 开头的最长上生子序列怎么算呢?把 \(a\) 序列倒过来(相当于reverse),然后给每个元素取反(乘以负一),对新得到的序列(暂且称其为 \(b\))求以 \(b_i\) 结尾的最长上升自序列,那么 \(a_i\) 开头的最长上升自序列的长度就等于以 \(b_{n-i+1}\) 结尾的最长上升自序列。
AC code(请不要介意我奇怪的码风):
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int T,n,a[N],f[N],dp[N],cnt,b[N],f2[N],dp2[N],cnt2;
int main(){
//freopen("xx.in","r",stdin);
//freopen("xx.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>T;
while(T--){
cnt=0;
cnt2=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
if(a[i]>f[cnt]){
f[++cnt]=a[i];
dp[i]=cnt;
}else{
int x=lower_bound(f+1,f+cnt+1,a[i])-f;
x=min(x,cnt);
dp[i]=x;
f[x]=a[i];
}
}
for(int i=1;i<=n;i++){
b[i]=a[n-i+1];
b[i]=-b[i];
}
f2[0]=INT_MIN;
for(int i=1;i<=n;i++){
if(b[i]>f2[cnt2]){
f2[++cnt2]=b[i];
dp2[i]=cnt2;
}else{
int x=lower_bound(f2+1,f2+cnt2+1,b[i])-f2;
x=min(x,cnt);
dp2[i]=x;
f2[x]=b[i];
}
}
int maxs=0;
for(int i=1;i<=n;i++){
maxs=max(maxs,dp[i]);
}
int cnt=0;
for(int i=1;i<=n;i++){
if(dp[i]+dp2[n-i+1]-1==maxs){
cnt++;
}
}
cout<<cnt<<"\n";
for(int i=1;i<=n;i++){
if(dp[i]+dp2[n-i+1]-1==maxs){
cout<<i<<" ";
}
}
cout<<"\n";
}
return 0;
}
附:人生第一次 Unknown Error,所以在AtCoder上交的
完结撒花!
标签:10,cnt,leq,int,ABC354F,Useless,LIS,序列,最長 From: https://www.cnblogs.com/wuyiming1263/p/18237982