BZOJ 1049 数字序列

Description

现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。

Input

第一行包含一个数n,接下来n个整数按顺序描述每一项的键值。

Output

第一行一个整数表示最少需要改变多少个数。 第二行一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

Sample Input

4
5 2 3 5

Sample Output

1
4

HINT

 

【数据范围】

90%的数据n<=6000。

100%的数据n<=35000。

保证所有数列是随机的。

 

Source

 

这道题感觉太神了。

令原数组为a。

对于第一问我们可以反过来思考——要求最少的改动的,不就是求最多的不改动的吗?

f[i]表示前i位最多的不改动的数字个数,f[i]=max(f[j]+1),j需要满足a[i]-a[j]≥i-j。对于条件,我们移一下项,化为a[i]-i≥a[j]-j,令b[i]=a[i]-i,不就是b[i]≥b[j]。细心的朋友一定看出来了,这不就是最长不下降子序列吗!!!O(nlogn)的求法上起。

第二问稍微麻烦一点,我们要知道一个结论:另g[i]表示前i个数,在改动数最少的前提下,最少改动的值。

那么对于所有合法的转移i,j(i>j,f[i]=f[j]+1,b[i]≥b[j]),最优解一定是在i与j之间某个k,k到j的值全为b[j],k+1到i的值全为b[i]。因此就可以dp了。证明自己脑补一下就可以了。。。(自己画画图,根据条件想想应该是可以明白的)

 

 1 #include<cstring>
 2 #include<vector>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<cstdio>
 6 #include<cstdlib>
 7 using namespace std;
 8 
 9 #define inf (1<<30)
10 #define maxn 35010
11 int tree[maxn],tot,n,f[maxn],a[maxn],bac[maxn];
12 long long g[maxn],s1[maxn],s2[maxn];
13 vector <int> vec[maxn];
14 
15 inline int lowbit(int x) { return x & -x; }
16 
17 inline void change(int a,int b) { for (;a <= tot;a += lowbit(a)) tree[a] = max(tree[a],b); }
18 
19 inline int calc(int a) { int ret = 0; for (;a;a -= lowbit(a)) ret = max(ret,tree[a]); return ret; }
20 
21 int main()
22 {
23     freopen("1049.in","r",stdin);
24     freopen("1049.out","w",stdout);
25     scanf("%d",&n);
26     for (int i = 1;i <= n;++i) scanf("%d",a+i),a[i] -= i,bac[++tot] = a[i];
27     a[++n] = inf; bac[++tot] = a[n];
28     sort(bac+1,bac+tot+1); tot = unique(bac+1,bac+tot+1) - bac - 1;
29     for (int i = 1;i <= n;++i)
30     {
31         int pos = lower_bound(bac+1,bac+tot+1,a[i]) - bac;
32         f[i] = calc(pos) + 1;
33         change(pos,f[i]);
34     }
35     printf("%d\n",n-f[n]);
36     for (int i = 0;i <= n;++i) vec[f[i]].push_back(i);
37     a[0] = -inf;
38     memset(g,0x7,sizeof(g)); g[0] = 0;
39     for (int i = 1;i <= n;++i)
40     {
41         int nn = vec[f[i] - 1].size();
42         for (int p = 0;p < nn;++p)
43         {
44             int j = vec[f[i]-1][p];
45             if (j >= i) break; if (a[i] < a[j]) continue;
46             for (int k = j;k <= i;++k) s1[k]=abs(a[k]-a[j]),s2[k]=abs(a[k]-a[i]);
47             for (int k = j+1;k <= i;++k) s1[k] += s1[k-1],s2[k] += s2[k-1];
48             for (int k = j;k < i;++k) g[i] = min(g[i],g[j]+s1[k]-s1[j]+s2[i]-s2[k]);
49         }
50     }
51     printf("%lld",g[n]);
52     fclose(stdin); fclose(stdout);
53     return 0;
54 }
View Code

 

posted @ 2015-02-17 10:03  lmxyy  阅读(243)  评论(0编辑  收藏  举报