树状数组套题

Contest

题目链接
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 5;
ll n, res, t[N];

struct Node {
    ll a, b, c;
} q[N];

bool cmpa(Node x, Node y) {
    return x.a < y.a;
}
bool cmpb(Node x, Node y) {
    return x.b < y.b;
}

void add(ll x) {
    while (x <= n) {
        t[x]++;
        x += x & -x;
    }
}

ll query(ll x) {
    ll sum = 0;
    while (x) {
        sum += t[x];
        x -= x & -x;
    }
    return sum;
}

int main() {
    cin >> n;
    for (ll i = 1; i <= n; i++)
        cin >> q[i].a >> q[i].b >> q[i].c;
    sort(q + 1, q + n + 1, cmpa);
    for (ll i = 1; i <= n; i++) {
        add(q[i].b);
        res += i - query(q[i].b);
    }
    memset(t, 0, sizeof(t));
    for (ll i = 1; i <= n; i++) {
        add(q[i].c);
        res += i - query(q[i].c);
    }
    sort(q + 1, q + n + 1, cmpb);
    memset(t, 0, sizeof(t));
    for (ll i = 1; i <= n; i++) {
        add(q[i].c);
        res += i - query(q[i].c);
    }
    cout << res / 2 << '\n';
    return 0;
}

题解:三维偏序,CDQ分治法,一维排序,二维归并(分治),三维树状数组(这里是树状数组)
首先一维排好序后,求(二维的逆序对数 + 三维的逆序对数)
然后二维排好序后,求(三维的逆序对数)
最终将结果/2就是答案

A - Japan

题目链接
在这里插入图片描述
在这里插入图片描述

#include <float.h>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <string>
#include <vector>

using namespace std;
typedef long long ll;
#define mod 1000000007
const int M = 1e3 + 10;
int t, n, m, k;
int c[M];
struct node {
    int u, v;
} e[M * M];
bool cmp(node a, node b) {
    if (a.u != b.u)
        return a.u < b.u;
    return a.v < b.v;
}
int lowbit(int x) {
    return x & (-x);
}
void update(int x, int y, int n) {
    for (int i = x; i <= n;
         i += lowbit(i))  // x为更新的位置,y为更新后的数,n为数组最大值
        c[i] += y;
}

int getsum(int x) {
    int ans = 0;
    for (int i = x; i; i -= lowbit(i))
        ans += c[i];
    return ans;
}

int main() {
    scanf("%d", &t);
    for (int j = 1; j <= t; ++j) {
        memset(c, 0, sizeof(c));
        ll ans = 0;
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 0; i < k; ++i) {
            scanf("%d%d", &e[i].u, &e[i].v);
        }
        sort(e, e + k, cmp);
        for (int i = 0; i < k; ++i) {
            ans += getsum(m) - getsum(e[i].v);
            update(e[i].v, 1, m);
        }
        printf("Test case %d: %lld\n", j, ans);
    }
    return 0;
}

题解:树状数组的板子题,这里题意是要算线段之间的交点有多少个,因为可能很多,要开longlong。端点分为左端点和右端点,连线只会在左端点和右端点之间连接,这里我用树状数组记录每个右端点所连接的线段数,给线段做个排序,依次从小到大,每次加入一条线段,便算一次交点和,这里我是把比右端点序号还大的其他右端点做一个和(这里的和指的是右端点此时此刻所连接的线段),之后依次加上即可。

B - Ping pong

题目链接

在这里插入图片描述
在这里插入图片描述

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#define lowbit(x) (x & (x ^ (x - 1)))
#define maxn 100000 + 5
using namespace std;
int n, m;
int c[maxn], a[maxn], b[maxn], d[maxn];
void update(int p) {
    while (p <= m) {
        c[p]++;
        p += lowbit(p);
    }
}
int sum(int p) {
    int ans = 0;
    while (p > 0) {
        ans += c[p];
        p -= lowbit(p);
    }
    return ans;
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        m = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            m = max(a[i], m);
        }
        memset(c, 0, sizeof(c));
        for (int i = 1; i <= n; i++) {
            update(a[i]);
            b[i] = sum(a[i] - 1);  ///第 i 个人前面比他小的人数,存入b数组中
        }
        memset(c, 0, sizeof(c));
        for (int i = n; i > 0; i--) {
            update(a[i]);
            d[i] = sum(a[i] - 1);  ///第 i 个人后面比他小的人数,存入b数组中
        }
        long long ans = 0;
        for (int i = 2; i < n; i++)
            ans += b[i] * (n - d[i] - i) + d[i] * (i - b[i] - 1);
        printf("%lld\n", ans);
    }
    return 0;
}

题解:题意是有不同等级的乒乓球员要打比赛,每场比赛必须有一名裁判,而裁判到他们的任何一个人的距离不应该超过选手之间的距离,而且裁判的水平应该在两名乒乓球员之间。这里枚举了裁判,寻找裁判左边比裁判等级高的和裁判右边比裁判等级低的两两匹配打,也要寻找裁判左边比裁判等级低得和裁判右边比裁判等级高的两两匹配打,再把两者加起来题解链接

珂朵莉的数列

题目链接
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define pii pair<int, int>
#define int __int128_t
const int inf = 0x3f3f3f3f;
const int maxn = 1001110;
const int M = 1e9 + 7;
int n, m;

int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + c - '0', c = getchar();
    return f * x;
}

void print(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x / 10)
        print(x / 10);
    putchar(x % 10 + '0');
}

int a[maxn], b[maxn], t[maxn];

int lowbit(int x)  //树状数组求前缀和
{
    return x & (-x);
}

void update(int i, int x) {
    while (i <= m) {
        t[i] += x;
        i += lowbit(i);
    }
}

int query(int x) {
    int res = 0;
    while (x) {
        res += t[x];
        x -= lowbit(x);
    }
    return res;
}

signed main() {
    n = read();
    for (int i = 1; i <= n; i++) {
        a[i] = read();
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    m = unique(b + 1, b + 1 + n) - b - 1;  //离散化
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        a[i] = lower_bound(b + 1, b + 1 + m, a[i]) - b;
        int x = m - a[i] + 1;  //反过来
        ans += (n - i + 1) * query(x - 1);
        update(x, i);
    }
    print(ans);
    putchar('\n');
    return 0;
}

题解:找规律,看看每个数字的贡献与下标有关题解链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值