Atcoder 1218 bzoj 4240 libreoj 2873 「JOISC 2014 Day1」有趣的家庭菜园 题解

题意简述

给定一个序列,长度为,你珂以交换序列中的两个数,使得序列满足:对于每个点,要么它所有左边的元素,要么它所有右边的元素。(形象的说,就是一个山峰)输出最少的交换次数。记得开

思路

倒序排序,一个一个插入,判断是插入在左边还是插入在右边,记录逆序对个数,求出答案

具体思路

这个问题看起来不好直接求。确实,这种题如果第一次做的话,是很难想到做法的。

首先确定这样一个东西:交换次数=把原序列看成之后交换得到的逆序对数。

然后我们要让这个值最小。接下来就是如何求这个最小了。

有这样一个模型,像这种要么左边,要么右边的题目,我们珂以这样考虑:

  1. 序列倒序
  2. 初始序列为空
  3. 考虑我们新加入的元素在最左边还是最右边,插入进去

正确性是显然的。由于我们在插入的时候,所有数都是的。所以我们不管插入在左边还是右边,还是能构成一个“山峰”。只不过是左边长还是右边长的问题了。

我们开一个结构体,把每个数按值(设为)排序,然后还要保留原来的位置(方便统计逆序数,设为)。

那么我们把插入在序列的最左边,花费就是新加入的逆序对数,也就是中满足的个数。同理,插入在最右边的代价就是中满足的个数。第一个很明显珂以用树状数组记录,设答案为。那么第二个就不用再去记录了,就是是数字的总数,由于显然不会有相等的情况,所以总数-小于的就是大于的。

然后比较一下是大还是大即珂,记录到答案。

注意,如果有连续的一段相等的话,那么要记得判一下。如果不判的话,我们就要记录一些类似“交换两个相同的数”的无用功了,就不一定最小。我们原来那个算法几乎完全是基于数值进行插入判断的,只是起到答案的作用。所以即使是相同的,由于不同,我们也会认为这是不一样的数,然后还会记录答案。这很明显是不必要的。

实现注意

千万不要忘了开!!!
千万不要忘了开!!!
千万不要忘了开!!!
千万不要忘了开!!!
千万不要忘了开!!!

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define int long long
#define N 355555
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
class BIT
{
public:
int tree[N];
int len;
void BuildTree(int _len)
{
len=_len;
FK(tree);
}
void Add(int pos,int val=1)
{
for(int i=pos;i<=len;i+=(i&(-i)))
{
tree[i]+=val;
}
}
int Query(int pos)
{
int ans=0;
for(int i=pos;i>0;i-=(i&(-i)))
{
ans+=tree[i];
}
return ans;
}
}T;

struct node
{
int h,pos;
}a[N];
bool operator<(node a,node b){return a.h>b.h;}
int n;
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n);
F(i,1,n)
{
R1(a[i].h);
a[i].pos=i;
}
}

void Soviet()
{
sort(a+1,a+n+1);
T.BuildTree(n);

int j;
int ans=0;
F(i,1,n)
{
j=i;
while(a[j].h==a[j+1].h and j<n) ++j;
F(k,i,j) ans+=min(T.Query(a[k].pos),i-1-T.Query(a[k].pos));
F(k,i,j) T.Add(a[k].pos);
i=j;
}

printf("%lld\n",ans);
}
void IsMyWife()
{
Input();
Soviet();
}
#undef int //long long
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}2

w