版权属于舒老师,想要引用此题(包括题面)的朋友请联系博主
分析:
mmp,这道题做了不短的时间(这是什么语法。。。)
首先我们可以用 O(nlogn) O ( n l o g n ) 的复杂度计算出 f[i] f [ i ] (表示以 i i 为结尾的LIS)
以下就是我yy的过程了:
一开始有一个非常naive的想法:
把数组的每个位置视为一个点
每个点向在ta后面且比ta大的点连一条边
那么对于询问我们只需要在图中找到一条长度为Q的路径即可
(至于字典序最小,我们直接贪心构造即可)
但是这样边数会比较多,我们深入考虑能成为每个点后继的条件:
看一个简单的例子
1 2 3 4 5
a= 2 5 6 4 3
对于,我们显然需要向
a[2]
a
[
2
]
连边,但是我们没有必要向
a[3]
a
[
3
]
连边
然而我们还是需要向
a[4],a[5]
a
[
4
]
,
a
[
5
]
连边
一句话总结:从前往后扫,记
minn
m
i
n
n
为当前扫到的数字的最小值,我们只需要向小于
minn
m
i
n
n
且大于
a[i]
a
[
i
]
的元素连边即可
(也就是说,数值较大还靠后的元素就没有必要连边了)
这样我们就可以得到每个结点的后继结点了
贪心的,后继结点按照数值从小到大排序,这样我们遍历的时候就可以直接贪心构造解了
至此为止,如果我们要处理询问就需要遍历整个图
虽然边数已经减少了很多,但是还是达不到要求
所以我用 O(n2) O ( n 2 ) 的时间处理出了 len[i] l e n [ i ] ,表示以位置 i i 为起点的
这样在构造解的时候,我们找到
len[i]>=Q
l
e
n
[
i
]
>=
Q
且
a[i]
a
[
i
]
最小的元素作为起点
之后贪心构造答案即可:遍历后继结点,只有后继结点的
“len+当前答案长度>=Q”
“
l
e
n
+
当
前
答
案
长
度
>=
Q
”
,我们才继续往下走
复杂度
O(nq)
O
(
n
q
)
(预处理不算啦)
可能有dalao会吐槽:
O(n2)
O
(
n
2
)
处理出
len
l
e
n
数组,不是太慢了吗
啊咧,确实。。。
不过这道题的标算是
O(nq)
O
(
n
q
)
的(所以胖爷用
O(nqlogn)
O
(
n
q
l
o
g
n
)
艹掉了标算。。。)
科学的预处理:
把整个序列翻转过来,求以每个结点为末尾的最长下降子序列
这样正过来就是以每个结点为起点的
LIS
L
I
S
实际上这道题的标解贪心贪的更漂亮
从前往后,找到
len>=Q
l
e
n
>=
Q
且数值最小的位置
i
i
,作为答案的起点
之后的每一位一定在之后,
我们在
i
i
之后找到且数值最小的位置
j
j
<script type="math/tex" id="MathJax-Element-57">j</script>,作为答案的第二位
以此类推即可
tip
贴出我在考场上写的丑陋代码(毕竟也是想了好久的)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10002;
int n,g[N],maxlen,m,V[N],a[N],pos[N],nn,len[N];
int G[N][1002],size[N];
int X;
void LIS() {
g[1]=a[1];
maxlen=1;
for (int i=2;i<=n;i++) {
if (a[i]>g[maxlen]) {
g[++maxlen]=a[i];
continue;
}
int r=lower_bound(g+1,g+1+maxlen,a[i])-g;
g[r]=a[i];
}
}
int cmp(const int &A,const int &B) {
return a[A]<a[B]; //按值排序
}
void prepare() {
for (int i=1;i<=n;i++) size[i]=0;
for (int i=n;i>=1;i--) {
len[i]=1;
int minn=n+1; //值大还靠后的就不用连边了
for (int j=i+1;j<=n;j++)
if (a[j]<minn&&a[j]>a[i]) {
G[i][++size[i]]=j; //G[i] 位置i的后继
minn=a[j];
len[i]=max(len[i],len[j]+1);
//len[i] 以i为起点的LIS
}
}
for (int i=1;i<n;i++) sort(G[i]+1,G[i]+1+size[i],cmp);
}
int ans[N];
bool flag;
void dfs(int t,int s) {
if (t>=X) {
flag=0;
return;
}
if (!flag) return;
for (int i=1;i<=size[s];i++)
if (len[G[s][i]]+t>=X&&flag) {
printf("%d ",V[a[G[s][i]]]);
dfs(t+1,G[s][i]);
if (!flag) return;
}
if (!flag) return;
}
void solve() {
flag=1;
for (int i=1;i<=nn-X+1;i++)
if (len[pos[i]]>=X&&flag) {
printf("%d ",V[a[pos[i]]]);
dfs(1,pos[i]);
printf("\n");
break;
}
}
int main() {
freopen("misswalmart.in","r",stdin);
freopen("misswalmart.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),V[i]=a[i]; //V[i] 记录i的原始值
sort(V+1,V+1+n);
nn=unique(V+1,V+1+n)-V-1;
for (int i=1;i<=n;i++) {
a[i]=lower_bound(V+1,V+1+nn,a[i])-V; //离散化
pos[a[i]]=(pos[a[i]]==0)? i:min(pos[a[i]],i); //值最靠前的位置
}
LIS();
prepare();
scanf("%d",&m);
for (int i=1;i<=m;i++) {
scanf("%d",&X);
if (X>maxlen) {printf("LXALWAYSMISS\n");continue;}
solve();
}
return 0;
}