【题解】Jealous Two ABC231F

题目来源: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 AiAj并且 B j ≥ B i B_j \geq B_i BjBi
两个条件不好满足,我们可以转化为一个条件。
即将 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 BjBi
然后问题就变成了 1 1 1 i i i之中,有多少 B j B_j Bj满足 B j ≥ B i B_j \geq B_i BjBi
这就是点 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(cntBjBi1ji)
乍一看——
这不就是逆序对嘛!
我们可以使用树状数组来解决。
首先离散化,代码十分简单:

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(ji)的元素个数,即计算 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) BjBi(ji)的元素个数。
之后,将 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 B4),是符合题意的方案。
时间复杂度为 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;
}

END

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值