2013编程之美挑战赛---无尽的编号

Description

在一条公路上,将要依次建造N座建筑。在每个建筑建成之后,都会用一个01串来给它编号。整条公路从起点到终点,所有建筑的编号都严格按照字典序递增的顺序来排列,而每在一个新的地方建起一个建筑时,它的编号会按以下规则确定:

1) 编号要比前一个建筑(起点方向)的字典序大,比后一个建筑(终点方向)的字典序小

3) 编号一定以1结尾

2) 编号要尽可能短,满足该条件时,字典序尽可能小

最开始时,公路的起点和终点上各有一个建筑,编号分别是0和1。接下来依次给出N个坐标 a1, a2, ..., aN,依次表示下一个建筑将要建造的位置,最后要问,当所有建筑建成时,这些建筑的编号总长度是多少,其中又出现了多少个字符1。所有建筑都在公路起点和终点之间,并且没有两个建筑建在同一个位置。


Input
输入文件包含多组测试数据。
第一行,给出一个整数T,为数据组数。接下来依次给出每组测试数据。
每组数据中第一行为一个整数 N,表示将要建造的建筑数量,第二行是用单个空格隔开的N个互不相同的整数 a 1, a 2, ..., a N,表示依次将要建造的建筑所在的坐标。

小数据:T ≤ 100, 0 < N ≤ 100, 0 ≤ a i ≤ 1000
大数据:T ≤ 10, 0 < N ≤ 50000, 0 ≤ a i ≤ 500000​
Output
对于每组测试数据,输出一行"Case #X: Y Z",其中X表示测试数据编号,Y表示所有建筑编号总长,Z表示所有编号中字符1的数量。所有建筑包括起点和终点的这两个建筑。所有数据按读入顺序从1开始编号。
Sample Input
1
5
1 2 3 4 5
Sample Output
Case #1: 22 16

解题思路

我们简单画几个样例来模拟一下规则,就可以看出其中的规律了。首先,考虑新建筑的编号的长度,它一定比相邻两个编号更长,再根据编号规则可以知道,它的长度一定是相邻编号长度较大值+1。其次,考虑新编号中1的个数,它总是恰好等于左边建筑的1的数量+1。找到这两个规律之后,我们便可以通过模拟来得到每个建筑的编号长度和其中1的个数。
代码参考rank 2的 猛犸也钻地 -大神也。
#include <iostream>
#include <map>
#include <utility>
#include <algorithm>
#include <cstddef>
#include <cstdlib>
using namespace std;
typedef pair<int,int> PII;
int main()
{
	int t,cas=1;
	cin>>t;
	while(t--)
	{
		int n,x;
		long long ans_len=2,ans_one=1;
		cin>>n;
		map<int,PII> mp;
		mp[-1]=make_pair(1,0);
		mp[500001]=make_pair(1,1);
		for(int i=0;i<n;i++)
		{
			cin>>x;
			map<int,PII>::iterator r=mp.upper_bound(x);
			map<int,PII>::iterator l=r--;
			int len=(l->second.first > r->second.first)?l->second.first:r->second.first;
			int ones=r->second.second;
			mp[x]=make_pair(++len,++ones);
			ans_len+=len;
			ans_one+=ones;
		}
		cout<<"Case #"<<cas++<<": ";
		cout<<ans_len<<" "<<ans_one<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值