休息(rest)

休 息 ( r e s t ) 休息(rest) (rest)

题目链接: j z o j   3462 jzoj\ 3462 jzoj 3462

题目

休息的时候,可以放松放松浑身的肌肉,打扫打扫卫生,感觉很舒服。在某一天,某 L M Z LMZ LMZ 开始整理他那书架。已知他的书有 n n n 本,从左到右按顺序排列。他想把书从矮到高排好序,而每一本书都有一个独一无二的高度 H i H_i Hi。他排序的方法是:每一次将所有的书划分为尽量少的连续部分,使得每一部分的书的高度都是单调下降,然后将其中所有不少于 2 2 2 本书的区间全部翻转。重复执行以上操作,最后使得书的高度全部单调上升。可是毕竟是休息时间, L M Z LMZ LMZ 不想花太多时间在给书排序这种事上面。因此他划分并翻转完第一次书之后,他想计算,他一共执行了多少次翻转操作才能把所有的书排好序。 L M Z LMZ LMZ 惊奇地发现,第一次排序之前,他第一次划分出来的所有区间的长度都是偶数。

输入

第一行一个正整数 n n n, 为书的总数。

接下来一行 n n n个数,第 i i i个正整数 H i H_i Hi,为第 i i i 本书的高度。

输出

仅一个整数,为 L M Z LMZ LMZ 需要做的翻转操作的次数。

样例输入

6

5 3 2 1 6 4

样例输出

3

样例解释

第一次划分之后,翻转 ( 5 , 3 , 2 , 1 ) (5,3,2,1) (5,3,2,1) ( 6 , 4 ) (6,4) 6,4。之后,书的高度为 1   2   3   5   4   6 1\ 2\ 3\ 5\ 4\ 6 1 2 3 5 4 6,然后便是翻转 ( 5 , 4 ) (5,4) 5,4即可。

数据范围

对于 10 % 10\% 10%的数据: n  ⁣ < =  ⁣ 50 n\!<=\!50 n<=50
对于 40 % 40\% 40%的数据: n  ⁣ < =  ⁣ 3000 n\!<=\!3000 n<=3000
对于 100 % 100\% 100%的数据: 1  ⁣ < =  ⁣ n  ⁣ < =  ⁣ 100000   ,   1  ⁣ < =  ⁣ H i  ⁣ < =  ⁣ n 1\!<=\!n\!<=\!100000\ ,\ 1\!<=\!H_i\!<=\!n 1<=n<=100000 , 1<=Hi<=n

思路

这道题其实挺结论题的,但是又要用到树状数组。

首先第一次,我们就按照正常的方式来模拟。
接着,我们就只要用树状数组的方式求出当前数组的逆序对个数,加上原来模拟时算出的个数,就是答案了。

至于为什么是逆序对,就是因为一开始模拟玩之后几个交换的区块都整齐了,就只有区块与区块之间的不整齐。那我们就要不停的交换临近的两个数,让那些不整齐的数交换。
那就是逆序对个数咯!

代码

#include <cstdio>
#include <algorithm>
#define ll long long

using namespace std;

struct node {
	ll x, num;
}a[200000];
ll c[200000], n, ans;

int cmp(node x, node y) {
	return x.x > y.x;
}

void add(int x) {//单点加值
	while (x <= n) {
		c[x]++;
		x += x & -x;
	}
}

int getsum(int x) {//单点求值
	int re = 0;
	while (x > 0) {
		re += c[x];
		x -= x & -x;
	}
	return re;
}

int main() {
	scanf("%d", &n);//读入
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i].x);//读入
		a[i].num = i;//记录坐标
	}
	
	for (int i = 1; i < n; i++) {//第一次直接暴力搞
		int j = i, k;
		while (a[i].x > a[i + 1].x && i < n) i++;
		k = i;
		bool yes = 0;
		while (j < k) {
			yes = 1;
			swap(a[j].x, a[k].x);
			j++;
			k--;
		}
		if (yes) ans++;	
	}
	
	sort(a + 1, a + n + 1, cmp);//排序
	
	for (int i = 1; i <= n; i++) {//求逆序对
		ans += getsum(a[i].num);
		add(a[i].num);
	}
	
	printf("%lld", ans);//输出
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮你设计这个程序。 首先,我们设计一个基类 `Worker`,其中包含两个虚函数 `work()` 和 `rest()`,分别表示工作和休息。代码如下: ```cpp class Worker { protected: string name; public: Worker(string name): name(name) {} virtual ~Worker() {} virtual void work() = 0; virtual void rest() { cout << name << " is resting." << endl; } }; ``` 然后,我们设计两个派生类 `Driver` 和 `Blacksmith`,分别实现 `work()` 函数。`Driver` 类中的 `work()` 表示司机开车,`Blacksmith` 类中的 `work()` 表示铁匠打铁。同时,`Blacksmith` 类中还有一个特有函数 `bath()`,表示洗澡。代码如下: ```cpp class Driver: public Worker { public: Driver(string name): Worker(name) {} ~Driver() {} void work() override { cout << name << " is driving." << endl; } }; class Blacksmith: public Worker { public: Blacksmith(string name): Worker(name) {} ~Blacksmith() {} void work() override { cout << name << " is blacksmithing." << endl; } void bath() { cout << name << " is bathing." << endl; } }; ``` 最后,在 `main` 函数中,我们创建一个指针数组,将 `Driver` 和 `Blacksmith` 类的对象指针存储在其中。然后,使用循环遍历该数组,依次调用 `work()`、`rest()` 和 `bath()` 函数(如果是 `Blacksmith` 类的对象)。同时,我们在每个类的构造函数和析构函数中加入输出语句,以便观察它们的调用细节。代码如下: ```cpp int main() { Worker* workers[] = { new Driver("Tom"), new Blacksmith("Jerry"), new Blacksmith("Bob") }; for (auto worker: workers) { worker->work(); worker->rest(); auto blacksmith = dynamic_cast<Blacksmith*>(worker); if (blacksmith) { blacksmith->bath(); } delete worker; } return 0; } ``` 输出结果如下: ``` Tom is driving. Tom is resting. Jerry is blacksmithing. Jerry is resting. Jerry is bathing. Bob is blacksmithing. Bob is resting. Bob is bathing. ``` 这样,我们就完成了这个程序的设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值