如果能想到贪心那么这道题就不难了。假设我们求出f[i]表示以i为第一个数的最长上升子序列长度,那么为了使字典序最小,我们可以采用这样的贪心策略:当前询问的长度为Q,我们枚举第一个i,使得f[i] >= Q && a[i] > last,找到i之后:Q–, last = a[i],然后继续枚举。这个策略的正确性是很显然的。读入一个Q之后先判断是否有解,因为如果存在一个长度为L的上升序列,那么必定存在长度为1~L-1的上升序列,所以只需要判断Q与最长上升序列长度即可。复杂度是O(NM)
那么剩下的问题就是求f数组了。求LIS的方法有很多,这道题给了10s,或许N^2能卡过。我使用的NlogN的做法是用线段树优化一下。需要先离散化。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define M 10005
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
#define LC lc,l,mid
#define RC rc,mid+1,r
#define va first
#define id second
using namespace std;
typedef pair<int,int> PII;
int n, m, L, R, C, Maxlen, a[M], A[M], f[M], tag[M<<2], mx[M<<2];
PII Sort[M];
void get(int &x)
{
char c = getchar(); x = 0;
while(c < '0' || c > '9') c = getchar();
while(c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
}
void put(int x)
{
char s[10]; int cnt = 0;
while(x) s[++cnt] = (x%10)+48, x /= 10;
while(cnt) putchar(s[cnt--]);
}
void pushdown(int p)
{
if(!tag[p]) return ;
tag[lc] = max(tag[lc], tag[p]);
tag[rc] = max(tag[rc], tag[p]);
mx[lc] = max(mx[lc], tag[lc]);
mx[rc] = max(mx[rc], tag[rc]);
tag[p] = 0;
}
void change(int p, int l, int r)
{
if(L <= l && r <= R)
{
tag[p] = max(tag[p], C);
mx[p] = max(tag[p], mx[p]);
return ;
}
pushdown(p);
if(L <= mid) change(LC);
if(R > mid) change(RC);
mx[p] = max(mx[lc], mx[rc]);
}
int query(int p, int l, int r)
{
if(L <= l && r <= R) return mx[p];
pushdown(p);
int res = 0;
if(L <= mid) res = max(res, query(LC));
if(R > mid) res = max(res, query(RC));
return res;
}
int main()
{
get(n);
for(int i = 1; i <= n; i++)
{
get(a[i]); A[i] = a[i];
Sort[i].va = a[i];
Sort[i].id = i;
}
sort(Sort+1, Sort+n+1);
int t = 0;
for(int i = 1; i <= n; i++)
{
if(Sort[i].va != Sort[i-1].va || !t) t++;
a[Sort[i].id] = t;
}
f[n] = 1;
L = a[n], R = n, C = f[n];
change(1, 1, n);
for(int i = n-1; i; i--)
{
L = a[i]+1, R = n;
f[i] = query(1, 1, n) + 1;
L = R = a[i], C = f[i]; change(1, 1, n);
}
L = 1, R = n; Maxlen = query(1, 1, n);
get(m);
while(m--)
{
int q; get(q);
if(q <= Maxlen)
{
int last = 0;
for(int i = 1; i <= n && q; i++)
if(a[i] > last && f[i] >= q)
{
last = a[i], q--;
put(A[i]);
if(q) putchar(' ');
}
}
else printf("Impossible");
if(m) putchar('\n');
}
return 0;
}
//纪念第一次在Linux下写题