[BZOJ2933]数据
2017-07-24 21:13
99 查看
题目描述
Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这 n 个待排序的数。例如:3 4 2 –1 4 1 2 3 4,就表示有两组数据。第一组有 3 个数(4,2,-1),第二组有 4个数(1,2,3,4)。可是现在 Mr_H 做的输入数据出了一些问题。例如:2 1 9 3 2 按理说第一组数据有 2 个数(1,9),第二组数据有 3 个数,可是“3”后面并没有出现三个数,只出现了一个数“2”而已!现在
Mr_H 需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1 或-1,写个程序,计算最少需要多少步才能将数据改得合法。
输入
第一行一个整数 m,表示 Mr_H 做的输入数据包含的整数个数。第二行包含 m 个整数 a[i],每个整数的绝对值不超过 10000。
输出
一个整数,表示把数据修改为合法的情况下,最少需要多少步。
样例输入
4
1 9 3 2
样例输出
2
提示
对于 20%的数据,m<=10, |a[i]|<=5;
对于 60%的数据,m<=5000, |a[i]|<=10000
对于 100%的数据,m<=100000, |a[i]|<=10000
题解:
dp[i]: 使num[1]~num[i]的数串合法, 最少需要多少步
dp[i]=min{ dp[j]+abs( num[j+1]-(i-j-1) ) } ( j<i )
时间为O( n^2 ),不加优化绝对T;
将abs分开算:
① dp[i]=min{ dp[j]+num[j+1]-i+j+1 } ( j<i≤num[j+1]+j+1 )
② dp[i]=min{ dp[j]+i-j-1-num[j+1] } ( j<i && num[j+1]+j+1<i )
[法一]用单调队列优化:
① dp[i]=min{ dp[j]+( num[j+1]+j+1 )-i } ( j<i≤num[j+1]+j+1 )
② dp[i]=min{ dp[j]-( num[j+1]+j+1 )+i } ( j<i && num[j+1]+j+1<i )
一开始先将num[j+1]+j+1处理出来。
先算①: 用优先队列维护, 开两个变量, 一个记录dp[j]+num[j+1], 一个记录num[j+1]
当算②时, 可直接由①变过来: ②=①-num[j+1]*2=q.top().a-q.top().b*2
[法二]用线段树优化:
Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这 n 个待排序的数。例如:3 4 2 –1 4 1 2 3 4,就表示有两组数据。第一组有 3 个数(4,2,-1),第二组有 4个数(1,2,3,4)。可是现在 Mr_H 做的输入数据出了一些问题。例如:2 1 9 3 2 按理说第一组数据有 2 个数(1,9),第二组数据有 3 个数,可是“3”后面并没有出现三个数,只出现了一个数“2”而已!现在
Mr_H 需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1 或-1,写个程序,计算最少需要多少步才能将数据改得合法。
输入
第一行一个整数 m,表示 Mr_H 做的输入数据包含的整数个数。第二行包含 m 个整数 a[i],每个整数的绝对值不超过 10000。
输出
一个整数,表示把数据修改为合法的情况下,最少需要多少步。
样例输入
4
1 9 3 2
样例输出
2
提示
对于 20%的数据,m<=10, |a[i]|<=5;
对于 60%的数据,m<=5000, |a[i]|<=10000
对于 100%的数据,m<=100000, |a[i]|<=10000
题解:
dp[i]: 使num[1]~num[i]的数串合法, 最少需要多少步
dp[i]=min{ dp[j]+abs( num[j+1]-(i-j-1) ) } ( j<i )
时间为O( n^2 ),不加优化绝对T;
将abs分开算:
① dp[i]=min{ dp[j]+num[j+1]-i+j+1 } ( j<i≤num[j+1]+j+1 )
② dp[i]=min{ dp[j]+i-j-1-num[j+1] } ( j<i && num[j+1]+j+1<i )
[法一]用单调队列优化:
① dp[i]=min{ dp[j]+( num[j+1]+j+1 )-i } ( j<i≤num[j+1]+j+1 )
② dp[i]=min{ dp[j]-( num[j+1]+j+1 )+i } ( j<i && num[j+1]+j+1<i )
一开始先将num[j+1]+j+1处理出来。
先算①: 用优先队列维护, 开两个变量, 一个记录dp[j]+num[j+1], 一个记录num[j+1]
当算②时, 可直接由①变过来: ②=①-num[j+1]*2=q.top().a-q.top().b*2
#include<iostream> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> using namespace std; const int INF=0x3f3f3f3f; const int N=1e5+10; struct node { int a, b; node(){} node( int x, int y ) { a=x; b=y; } bool operator < ( const node &x ) const { return a>x.a; } }; priority_queue< node >q; int n, dp , num , minz; int main() { scanf( "%d", &n ); for( int i=1; i<=n; i++ ) scanf( "%d", &num[i] ), num[i]+=i; q.push( node( num[1], num[1] ) ); memset( dp, INF, sizeof dp ); minz=INF; for( int i=1; i<=n; i++ ) { while( !q.empty() && q.top().b<i ) minz=min( minz, q.top().a-q.top().b*2 ), q.pop(); if( !q.empty() ) dp[i]=min( dp[i], q.top().a-i ); dp[i]=min( dp[i], minz+i ); q.push( node( dp[i]+num[i+1], num[i+1] ) ); } printf( "%d\n", dp ); return 0; }
[法二]用线段树优化:
#include<iostream> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; const int N=1e5+10; int n, num , ans; struct node{ int l, r, min1, min2; }tree[N<<2]; void Build( int l, int r, int pos ) { tree[pos].l=l; tree[pos].r=r; tree[pos].min1=tree[pos].min2=INF; if( l==r ) return; int mid=( l+r )>>1; Build( l, mid, pos<<1 ); Build( mid+1, r, pos<<1|1 ); } void Insert( int x, int pos, int d ) { if( tree[pos].r<x || x<tree[pos].l ) return; tree[pos].min1=min( tree[pos].min1, d-x ); tree[pos].min2=min( tree[pos].min2, d+x ); if( tree[pos].l==tree[pos].r ) return; Insert( x, pos<<1, d ); Insert( x, pos<<1|1, d ); } int Find( int l, int r, int pos, bool flg ) { if( tree[pos].r<l || r<tree[pos].l ) return INF; if( l<=tree[pos].l && tree[pos].r<=r ) { if( flg ) return tree[pos].min1; return tree[pos].min2; } return min( Find( l, r, pos<<1, flg ), Find( l, r, pos<<1|1, flg ) ); } int main() { scanf( "%d", &n ); for( int i=1; i<=n; i++ ) scanf( "%d", &num[i] ), num[i]+=i; Build( 1, n+1, 1 ); Insert( n+1, 1, 0 ); for( int i=n; i; i-- ) { ans=min( Find( i+1, min( num[i], n+1 ), 1, 1 )+num[i]+1, Find( num[i]+1, n+1, 1, 0 )-num[i]-1 ); Insert( i, 1, ans ); } printf( "%d\n", ans ); return 0; }
相关文章推荐
- BZOJ 2594 [Wc2006]水管局长数据加强版
- bzoj 2594: 水管局长数据加强版 Link-Cut-Tree
- [BZOJ2594] [Wc2006]水管局长数据加强版(LCT + kruskal + 离线)
- 【DFS好题】BZOJ1999- [Noip2007]Core树网的核(数据加强版)
- 【BZOJ2428】均分数据(模拟退火)
- bzoj 2428 [HAOI2006]均分数据
- bzoj1150: [CTSC2007]数据备份Backup 贪心
- 【BZOJ 1150】[CTSC2007]数据备份Backup
- bzoj 1150: [CTSC2007]数据备份Backup 贪心&堆
- [HAOI2006][BZOJ2428] 均分数据
- 数据结构:(平衡树,链表)BZOJ 1588[HNOI2002]营业额统计
- BZOJ 2594: [Wc2006]水管局长数据加强版
- BZOJ 2594 [Wc2006]水管局长数据加强版 LCT
- bzoj 2594: [Wc2006]水管局长数据加强版
- 洛谷P2503 BZOJ2428 [HAOI2006]均分数据
- 【BZOJ2428】[HAOI2006]均分数据
- BZOJ 2594 水管局长数据加强版(动态树)
- [BZOJ 2594] [Wc2006]水管局长数据加强版 【LCT】
- BZOJ 2190 - 欧拉函数的应用(数据范围不同 -> 做法不同 -> 启示)
- BZOJ3110 [ZJOI2013] K大数查询(加强数据)