题目来源:atcoder abc231 f
题目大意
一共有 N N N种糖,小A对第 i i i种糖果的喜爱度为 A i A_i Ai,小B对第 i i i种糖果的喜爱度为 B i B_i Bi,你要送给两人一人一颗糖果,当小A发现他更喜欢小B手上的糖果时,他会打架。小B亦然。求有多少种送糖的方法使两人不会打架。
思路
观察题目,概括得出:
设小A得到
i
i
i,小B得到
j
j
j
则
A
i
≥
A
j
A_i \geq A_j
Ai≥Aj并且
B
j
≥
B
i
B_j \geq B_i
Bj≥Bi
两个条件不好满足,我们可以转化为一个条件。
即将
A
A
A数组排序,此时枚举
A
i
A_i
Ai,可知小A后的糖果的
A
A
A值大于
A
i
A_i
Ai,所以小B不能选
i
i
i以后的糖果。
那么小A选择了
A
i
A_i
Ai,小B选的糖果要满足什么条件呢?
小B选的糖果
j
j
j要满足
B
j
≥
B
i
B_j \geq B_i
Bj≥Bi。
然后问题就变成了在
1
1
1到
i
i
i之中,有多少
B
j
B_j
Bj满足
B
j
≥
B
i
B_j \geq B_i
Bj≥Bi
这就是点
i
i
i对答案的贡献,即
a
n
s
+
=
c
n
t
(
c
n
t
表
示
B
j
≥
B
i
的
数
量
,
1
≤
j
≤
i
)
ans+=cnt(cnt表示B_j \geq B_i的数量,1\leq j\leq i)
ans+=cnt(cnt表示Bj≥Bi的数量,1≤j≤i) 。
乍一看——
这不就是逆序对嘛!
我们可以使用树状数组来解决。
首先离散化,代码十分简单:
void mark() {
int t = 1, tmp = p[1].b;
p[1].b = 1;
for (int i = 2; i <= n; i++) {
if (p[i].b != tmp)
t++;
tmp = p[i].b, p[i].b = t;
}
}
按
B
B
B数组从小到大排序,将他们重标号为
1
1
1到
t
t
t。
然后按
A
A
A数组从小到大排序,得到了一个有序的
A
A
A,离散化、无序的
B
B
B。
以下是思路的核心:
树状数组
t
r
[
i
]
tr[i]
tr[i]维护值为
i
i
i的数的个数,枚举到
i
i
i后,先求
B
j
<
B
i
(
j
≤
i
)
B_j< B_i(j\leq i)
Bj<Bi(j≤i)的元素个数,即计算
1
1
1到
B
i
B_i
Bi的总和,用
i
i
i减去这个总和,即为
B
j
≥
B
i
(
j
≤
i
)
B_j\geq B_i(j\leq i)
Bj≥Bi(j≤i)的元素个数。
之后,将
t
r
[
B
i
]
tr[B_i]
tr[Bi]加
1
1
1,维护树状数组。
这里要注意,当
B
i
=
=
B
j
B_i==B_j
Bi==Bj 且
A
i
=
=
A
j
A_i==A_j
Ai==Aj时,要再加上一些值,因为重复的一组会多出一些方案。
特别的,对
A
A
A排序时要双关键字,第一关键字为
A
i
<
A
j
A_i<A_j
Ai<Aj ,第二关键字为
B
i
>
B
j
B_i>B_j
Bi>Bj,因为当
A
i
=
=
A
j
A_i==A_j
Ai==Aj时,
B
B
B值较小的贡献中包含较大的,如:
A:3 3
B:4 2
而如果排序是这样的:
A:3 3
B:2 4
则程序计算答案为
0
0
0,但实际上,小A获得第
2
2
2种糖果,小B获得第
1
1
1种糖果(
B
值
为
4
B值为4
B值为4),是符合题意的方案。
时间复杂度为
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN)
code
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
typedef long long LL;
const int N = 200005;
int n;
LL ans = 0, tr[N];
struct present {
int a, b;
} p[N];
bool cmpa(present T1, present T2) { return T1.a < T2.a || (T1.a == T2.a && T1.b > T2.b); }
bool cmpb(present T1, present T2) { return T1.b < T2.b; }
void mark() {//离散化重标号
int t = 1, tmp = p[1].b;
p[1].b = 1;
for (int i = 2; i <= n; i++) {
if (p[i].b != tmp)
t++;
tmp = p[i].b, p[i].b = t;
}
}
//树状数组
void update(int val, int pl) {
for (; pl < N; pl += lowbit(pl)) tr[pl] += val;
}
LL getsum(int pl) {
LL sum = 0;
for (; pl; pl -= lowbit(pl)) sum += tr[pl];
return sum;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &p[i].a);
for (int i = 1; i <= n; i++) scanf("%d", &p[i].b);
sort(p + 1, p + n + 1, cmpb);
mark();
sort(p + 1, p + n + 1, cmpa);
LL tmpa = -1, tmpb = -1, cnt = 1;
for (int i = 1; i <= n; i++) {//计算贡献
ans += i - getsum(p[i].b - 1);
update(1, p[i].b);
if (p[i].b != tmpb || p[i].a != tmpa)
tmpb = p[i].b, tmpa = p[i].a, ans += cnt * (cnt - 1) / 2, cnt = 1;
else
cnt++;
}
ans += cnt * (cnt - 1) / 2;
printf("%lld", ans);
return 0;
}