休息(rest)
题目描述:
休息的时候,可以放松放松浑身的肌肉,打扫打扫卫生,感觉很舒服。在某一天,某LMZ 开始整理他那书架。已知他的书有n 本,从左到右按顺序排列。他想把书从矮到高排好序,而每一本书都有一个独一无二的高度Hi。他排序的方法是:每一次将所有的书划分为尽量少的连续部分,使得每一部分的书的高度都是单调下降,然后将其中所有不少于2 本书的区间全部翻转。重复执行以上操作,最后使得书的高度全部单调上升。可是毕竟是休息时间,LMZ 不想花太多时间在给书排序这种事上面。因此他划分并翻转完第一次书之后,他想计算,他一共执行了多少次翻转操作才能把所有的书排好序。LMZ 惊奇地发现,第一次排序之前,他第一次划分出来的所有区间的长度都是偶数。
输入:
第一行一个正整数n, 为书的总数。
接下来一行n个数,第i个正整数Hi,为第i 本书的高度。
输出:
仅一个整数,为LMZ 需要做的翻转操作的次数。
这道题可以先手动翻一轮。
然后我们可以发现只有两两翻转区间的端点有可能有问题(没排好)。
于是显然我们会考虑两两交换。
但是有可能会牵扯出其他的不合法的东西。
然后又不可能让你一个一个翻。所以还不如直接求逆序对。
然后考场想联系一下线段树。然后就写了线段树的做法。
时候来计算后发现要开所以就直接全部开了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll N = 100010;
inline void read(ll &x)
{
char ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';) ch = getchar();
for(;ch >= '0' && ch <= '9';) x = x * 10 + (ch ^ '0'), ch = getchar();
}
ll t[N << 2];
ll n,a[N],b[N],ans = 0,cnt = 0;
#define ls p << 1
#define rs ls | 1
void insert(ll p,ll x,ll tl,ll tr)
{
if(tl > tr) return;
if(tl == tr) { t[p] = 1; return; }
ll mid = tl + tr >> 1;
x <= mid ? insert(ls,x,tl,mid) : insert(rs,x,mid + 1,tr);
t[p] = t[ls] + t[rs];
}
ll query(ll p,ll l,ll r,ll tl,ll tr)
{
if(tl > tr) return 0;
if(l <= tl && tr <= r) return t[p];
ll mid = tl + tr >> 1,ret = 0;
if(mid >= l) ret += query(ls,l,r,tl,mid);
if(mid < r) ret += query(rs,l,r,mid + 1,tr);
return ret;
}
int main()
{
read(n); cnt = 0; a[0] = N;
for(int i = 1;i <= n; ++ i) read(a[i]);
for(int l = 1,r = 1;r <= n && l <= n;cnt = 0)
{
while((a[r] < a[r - 1] || l == r) && r <= n) b[++cnt] = a[r], ++r;
sort(b + 1,b + 1 + cnt);
for(int i = 1;i <= cnt; ++ i) a[i + l - 1] = b[i];
l = r; ++ans;
}
for(int i = n;i >= 1; -- i)
a[i] > 1 && (ans += query(1,1,a[i] - 1,1,n)), insert(1,a[i],1,n);
printf("%lld",ans);
return 0;
}
当然还有树状数组和归并排序的做法。这里便不再赘述。