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 }

时间: 2024-12-28 10:29:51

BZOJ 1049 数字序列的相关文章

BZOJ 1049 数字序列(LIS)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1049 题意:给出一个数列A,要求:(1)修改最少的数字使得数列严格递增:(2)在(1)的基础上使得修改的绝对值之和最小. 思路:对于第一问看起来像是求最长上升子 列,其实不是.我们想,若对于i<j,j能由i转移过来,那么需满足A[j]-A[i]>=j-i才行,这样我们发现只要A[j]-j& gt;=A[i]-i即可.因此令A[i]=A[i]-i,这样求LIS即可.对于第二问,

【BZOJ】【1049】【HAOI2006】数字序列

DP 第一问比较水……a[i]-=i 以后就变成最长不下降子序列问题了,第二问这个结论好神奇,考试的时候怎么破?大胆猜想,不用证明?TAT 题解:http://pan.baidu.com/share/link?uk=2651016602&shareid=1490516411 没有将a[0]置为-INF在BZOJ上WA了……so sad…… 1 /************************************************************** 2 Problem: 10

【BZOJ 1049】 [HAOI2006]数字序列

1049: [HAOI2006]数字序列 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 979  Solved: 389 [Submit][Status] Description 现在我们有一个长度为n的整数序列A.但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列.但是不希望改变过多的数,也不希望改变的幅度太大. Input 第一行包含一个数n,接下来n个整数按顺序描述每一项的键值. Output 第一行一个整数表示最少需要改变多少

【bzoj1049】【HAOI2006】【数字序列】

1049: [HAOI2006]数字序列 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 1117 Solved: 454 [Submit][Status][Discuss] Description 现在我们有一个长度为n的整数序列A.但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列.但是不希望改变过多的数,也不希望改变的幅度太大. Input 第一行包含一个数n,接下来n个整数按顺序描述每一项的键值. Output 第一行一个整数表示

bzoj 1858: [Scoi2010] 序列操作 题解

[原题] 1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 1031  Solved: 529 [Submit][Status] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内

BZOJ 1833 数字计数(统计[a,b]每个数字出现次数)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1833 题意:给定区间[a,b].求区间内0到9每个数字出现的次数. 思路:f[i][j]表示到后i位是否全 0(j=1表示i位之前全0)这个状态某个数字出现的次数,p[i][j]表示这个状态后面有多少个数字.那么当前枚举到的数字为要统计的数字时,答案加 上后面还有多少种数字,即下一个状态的p值.那么我们枚举要统计的数字依次统计即可. i64 f[20][2],p[20][2]; i64

生成数字序列命令(7)

生成数字序列命令:seq,shuf 7.1.seq 功能:打印数字序列 语法:seq [OPTION]... LAST seq [OPTION]... FIRST LAST seq [OPTION]... FIRST INCREMENT LAST 常用选项: -f  使用printf样式格式 -s  指定分隔符,默认换行符\n -w  等宽,用0填充 示例: 数字序列: 方法1: [[email protected] ~]# seq 10 1 2 3 4 5 6 7 8 9 10 方法2: fo

笔试算法题(34):从数字序列中寻找仅出现一次的数字 &amp; 最大公约数(GCD)问题

出题:给定一个数字序列,其中每个数字最多出现两次,只有一个数字仅出现了一次,如何快速找出其中仅出现了一次的数字: 分析: 由于知道一个数字异或操作它本身(X^X=0)都为0,而任何数字异或操作0都为它本身,所以当所有的数字序列都异或操作之后,所有出现两次的数字异或操作之后的结果都为0,则最后剩下的结果就是那个仅出现了一次的数字: 如果有多个数字都仅仅出现了一次,则上述的异或操作方法不再适用:如果确定只有两个数字只出现了一次,则可以利用X+Y=a和XY=b求解: 解题: 1 int findSin

求一组数字序列的分布情况(java)

最近需要做一个正态分布的函数图像所以要处理一段double序列 写了这个算法  先上效果图: 核心思想: 1先根据步长计算每一个区间 2循环进行判断序列中每个数属于哪个区间 3用一个数组来保存每一个区间中 数的个数 这样就可以得到整个分布函数了 当然效率值得考虑 我的机器1百万以上的数据就会有问题了 这是一个double类型的例子 int型就更容易啦 上代码! 1 package com.huang.distribution; 2 3 import java.math.BigDecimal; 4