??a title="[HAOI2007]上升序列
" >[HAOI2007]上升序列
预处理:设F[i]Zi开头的最长上升序列的长度Q怎么求不用说了吧?#8230;…
假设目前需要求长度为M的、标号字典序最的上升序列Q显然其W一个元素A[i]必须满F[i]>=MQ注意,不是{于Q是大于{于Q)Q找到满个条件的最的i卛_。然后,讄前已l求Z该序列的Wx个元素ؓA[y]Q则W?x+1)个元素A[z]需要满的条g是A[z]>A[y]Q且F[z]=F[y]-1Q找到满个条件的最的z即ؓ该序列的W?x+1)个元素。按照这U方法,扫描一遍就可以求出整个序列Q时间复杂度为O(N)。如果整个序列的最长上升序列长?lt;MQ则无解?br />
代码Q?
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define re(i, n) for (int i=0; i<n; i++)
#define re1(i, n) for (int i=1; i<=n; i++)
#define re2(i, l, r) for (int i=l; i<r; i++)
#define re3(i, l, r) for (int i=l; i<=r; i++)
#define rre(i, n) for (int i=n-1; i>=0; i--)
#define rre1(i, n) for (int i=n; i>0; i--)
#define rre2(i, r, l) for (int i=r-1; i>=l; i--)
#define rre3(i, r, l) for (int i=r; i>=l; i--)
#define ll long long
const int MAXN = 10010, MAXM = 1010, INF = ~0U >> 2;
int n, m, len, A[MAXN], F[MAXN], D[MAXN], res[MAXM];
void prepare()
{
D[len = 0] = INF; int l, r, mid;
rre(i, n) if (A[i] < D[len]) D[F[i] = ++len] = A[i]; else {
l = 0; r = len;
while (l < r) {
mid = l + r + 1 >> 1;
if (A[i] < D[mid]) l = mid; else r = mid - 1;
}
F[i] = l + 1; D[l + 1] = A[i];
}
}
void solve()
{
int x, y;
re(i, n) if (F[i] >= m) {
res[0] = A[i]; if (m == 1) return; x = m - 1; y = 1;
re2(j, i+1, n) if (F[j] >= x && A[j] > res[y - 1]) {res[y++] = A[j]; if (y == m) return; else x--;}
}
}
int main()
{
scanf("%d", &n); re(i, n) scanf("%d", &A[i]);
prepare();
int m_s; scanf("%d", &m_s);
re(i, m_s) {scanf("%d", &m); if (m > len) puts("Impossible"); else {solve(); re(j, m-1) printf("%d ", res[j]); printf("%d\n", res[m - 1]);}}
return 0;
}
??a title="[HAOI2006]数字序列
" >[HAOI2006]数字序列
首先Q由于序列的所有元素都是整敎ͼ所以可以将原序列的所有元素减d的下标,q样把上升序列转化Z下降序列了?br />W一问的l果昄是(N-新序列的最长不下降序列长度)。关键在于第二问。以下A均表C新序列?br />设F[i]ZA[i]l尾的最长不下降序列长度Q同P求法不用说了Q,G[i]为在A[i]不修改的前提下将A[0..i]转变Z下降序列的最修攚w。首先求出F[i]Q然后在求G[i]Ӟ枚D上一?#8220;不动?#8221;Q就是不修改的元素)A[j]Q显然必LA[j]<=A[i]且F[j]=F[i]-1Q,q样最修攚w是G[j]+(A[j..i]转变Z下降序列的最修攚wQ。可以证明,A[j..i]的最优修Ҏ案必然是A[j+1..t]全部修改为A[j]QA[t+1..i]全部修改为A[i]Q这里t是一个[j..i]范围的倹{问题就是如何求出最优的tQ?br />一开始,假设t=jQ即把A[j+1..i-1]全部修改为A[i]Q计出修改量,设ؓS。然后,׃A[j+1..i-1]之间的元素要么小于A[j]Q要么大于A[i]Q这个是昄的囧Q,我们把小于A[j]的元素称?#8220;数”Q把大于A[i]的元素称?#8220;大数”Q则当t取t0Ӟ修改量ؓS-(A[i]-A[j])*(A[j+1..t0]中的“数”个数减去“大数”个数Q。这P只需扫描一下,求出使得(A[j+1..t0]中的“数”个数减去“大数”个数Q值最大的t0卛_?br />当然q有一个问题,对于同一个iQ满?#8220;A[j]<=A[i]且F[j]=F[i]-1”的元素个数可能有很多Q如果一个一个枚举,一个一个扫描,会很慢的?#8230;…解决Ҏ是,求出满q个条g的j中最的一个,设ؓj0Q然后把A[j0+1..i-1]中的所?#8220;数”?#8220;大数”全部处理出来Q然后用cM前缀和的Ҏp搞了?#8230;…当然Qؓ了找到j0Q需要徏一个二分图Q边?F[i], i)?br />最后,Z方便Q可以把A序列的左边加一?INFQ右边加一?INF。最后ȝ旉复杂度,理论上ؓO(N2)Q但׃是随机数据,所以远q达不到q个U别?br />
代码Q?
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define re(i, n) for (int i=0; i<n; i++)
#define re1(i, n) for (int i=1; i<=n; i++)
#define re2(i, l, r) for (int i=l; i<r; i++)
#define re3(i, l, r) for (int i=l; i<=r; i++)
#define rre(i, n) for (int i=n-1; i>=0; i--)
#define rre1(i, n) for (int i=n; i>0; i--)
#define rre2(i, r, l) for (int i=r-1; i>=l; i--)
#define rre3(i, r, l) for (int i=r; i>=l; i--)
#define ll long long
const int MAXN = 40010, INF = ~0U >> 2;
struct edge {
int a, b, pre, next;
} E[MAXN << 1];
int n, m, A[MAXN], D[MAXN], F[MAXN], W[MAXN], res1;
ll G[MAXN], res2;
void init_d()
{
re(i, n) E[i].pre = E[i].next = i; m = n;
}
void add_edge(int a, int b)
{
E[m].a = a; E[m].b = b; E[m].pre = E[a].pre; E[m].next = a; E[a].pre = m; E[E[m].pre].next = m++;
}
void init()
{
scanf("%d", &n);
A[0] = -INF; re1(i, n) {scanf("%d", &A[i]); A[i] -= i;} A[++n] = INF; n++;
}
void solve()
{
init_d(); F[0] = 0; G[0] = 0; D[0] = -INF; add_edge(0, 0); int len = 0, l, r, mid, x, maxw; ll sum, tmp;
re2(i, 1, n) {
if (A[i] >= D[len]) D[F[i] = ++len] = A[i]; else {
l = 0; r = len;
while (l < r) {
mid = l + r + 1 >> 1;
if (A[i] >= D[mid]) l = mid; else r = mid - 1;
}
D[F[i] = ++l] = A[i];
}
for (int p=E[F[i]-1].next; ; p=E[p].next) if (A[i] >= A[x = E[p].b]) break;
W[x] = 0; re2(j, x+1, i) if (A[j] < A[i]) W[j] = W[j - 1] + 1; else W[j] = W[j - 1] - 1;
sum = 0; maxw = -INF; G[i] = ~0Ull >> 2;
rre2(j, i, x) {
if (A[j] <= A[i] && F[j] == F[i] - 1) {
tmp = G[j] + sum; if (tmp < G[i]) G[i] = tmp;
tmp = G[j] + sum - (ll) (maxw - W[j]) * (A[i] - A[j]); if (tmp < G[i]) G[i] = tmp;
}
if (A[j] > A[i]) sum += A[j] - A[i]; else sum += A[i] - A[j];
if (W[j] > maxw) maxw = W[j];
}
add_edge(F[i], i);
}
res1 = n - F[n - 1] - 1; res2 = G[n - 1];
}
void pri()
{
cout << res1 << endl << res2 << endl;
}
int main()
{
init();
solve();
pri();
return 0;
}

]]>