每日一题39. Contest(树状数组 / 容斥分治)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一题39. Contest(树状数组 / 容斥分治)相关的知识,希望对你有一定的参考价值。

补题链接:Here

算法涉及:树状数组、CDQ分治

n支队伍一共参加了三场比赛。
一支队伍x认为自己比另一支队伍y强当且仅当x在至少一场比赛中比y的排名高。
求有多少组(x,y),使得x自己觉得比y强,y自己也觉得比x强。
(x, y), (y, x)算一组。


【方案一】树状数组求逆序数

我们转换问题,x要比y最少一门排名高,y也对x同样如此。
那么就变成了,x有2门比y排名高,或者x2门比y排名低。因为排名不可能相同。
那么题目就变成了求逆序数问题,可以按照某一门排名去放另外一门成绩,求到的逆序数就是只看这两门的情况。
那么对于合理的x,y找到排名高,和排名低各一次,直接把答案除以2。

using ll = long long;
const int N = 2e5 + 10;
int a[N], b[N], c[N];
int tmp[N], sum[N];
int n;
int lowbit(int x) {return x & (-x);}
void add(int p, int val) {
    for (; p <= n; p += lowbit(p))sum[p] += val;
}
ll query(int i) {
    ll ans = 0;
    for (; i; i -= lowbit(i))ans += sum[i];
    return ans;
}
ll check(int a[], int b[]) {
    memset(sum, 0, sizeof(sum));
    for (int i = 1; i <= n; ++i)tmp[a[i]] = b[i];
    ll ans = 0;
    for (int i = 1; i <= n; ++i) {
        ans += query(n) - query(tmp[i]);
        add(tmp[i], 1);
    }
    return ans;
}
void solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)cin >> a[i] >> b[i] >> c[i];
    cout << (check(a, b) + check(a, c) + check(b, c)) / 2;
}

【方案二】容斥思想

容斥思想, 不考虑任何限制,总共有 \\(p =n(n - 1) / 2\\) 种组合
我们要求题目给的限制方案不好求,逆向思维求不符合的情况
显然不符合的情况是一个三维偏序 \\(a[i] > a[j], b[i] > b[j], c[i] > c[j]\\)

三维偏序问题可以用 cdq 分治去解决,这里就不赘述了
得到三维偏序的方案为 res
那么答案就是 p - res

using ll = long long;
struct node {
    int x, y, z;
    bool operator < (const node &s) {
        return x < s.x;
    }
} a[200005], b[200005];
ll p;
ll t[200005];
int lowbit(int x) {
    return x & (-x);
}

void add(int x, int y) {
    while (x < 200005) {
        t[x] += y;
        x += lowbit(x);
    }
}

ll sum(int x) {
    ll ans = 0;
    while (x) {
        ans += t[x];
        x -= lowbit(x);
    }
    return ans;
}

void cdq(int l, int r) {
    if (l == r) return ;
    int mid = l + r >> 1;
    cdq(l, mid); cdq(mid + 1, r);
    int L = l, R = mid + 1, tot = l;
    while (L <= mid && R <= r) {
        if (a[L].y <= a[R].y) add(a[L].z, 1), b[tot++] = a[L++];
        else p -= sum(a[R].z), b[tot++] = a[R++];
    }
    while (L <= mid) {
        add(a[L].z, 1);
        b[tot++] = a[L++];
    }
    while (R <= r) {
        p -= sum(a[R].z);
        b[tot++] = a[R++];
    }
    for (int i = l; i <= mid; i++) add(a[i].z, -1);
    for (int i = l; i <= r; i++) a[i] = b[i];
}
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].x >> a[i].y >> a[i].z;
    }
    p = 1LL * n * (n - 1) / 2;
    sort(a + 1, a + 1 + n);
    cdq(1, n);
    cout << p << "\\n";
    return 0;
}

以上是关于每日一题39. Contest(树状数组 / 容斥分治)的主要内容,如果未能解决你的问题,请参考以下文章

算法每日一题

算法每日一题

《LeetCode之每日一题》:230.有效的井字游戏

POJ 2155 Matrix(树状数组+容斥原理)

4.4 省选模拟赛 拉格朗日计数 树状数组+容斥.

AtCoder Beginner Contest 206 E - Divide Both(容斥)