合适数对(树状数组 || 归并)

合适数对

[Link](4316. 合适数对 - AcWing题库)

题意

在这里插入图片描述

思路

  • 树状数组

s i s_i si a i a_i ai的前缀和,等价于 s r − s l − 1 < t → s r − t < s l − 1 s_r-s_{l-1}<t \to s_r-t<s_{l-1} srsl1<tsrt<sl1,就是对于每一个 s i s_i si s 0 到 s i − 1 s_0到s_{i-1} s0si1中有多少个大于 s r − t s_r-t srt的数,由于值域太大我们先对所有可能用到的数进行离散化,然后对值域进行动态前缀和即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 4e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
LL n, m, k, t;
vector<LL> ve;
int g(LL x) {
    return lower_bound(ve.begin(), ve.end(), x) - ve.begin() + 1;
}
int tr[N];
int lowbit(int x) {
    return x & -x;
}
void add(int x) {
    for ( ; x < N; x += lowbit(x)) tr[x] ++;
}
int sum(int x) {
    int res = 0;
    for (; x; x -= lowbit(x)) res += tr[x];
    return res;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> t;
    vector<LL> s(n + 1);
    for (int i = 1; i <= n; i ++) cin >> s[i], s[i] += s[i - 1], ve.push_back(s[i]), ve.push_back(s[i] - t);
    ve.push_back(0), ve.push_back(-t);

    sort(ve.begin(), ve.end());
    ve.erase(unique(ve.begin(), ve.end()), ve.end());

    LL res = 0;
    add(g(0));
    for (int i = 1; i <= n; i ++) {
        res += i - sum(g(s[i] - t));
        add(g(s[i]));
    }

    cout << res << '\n';
    return 0;
}
  • 归并排序

我们要找的是 s r − s l < t s_r-s_l<t srsl<t的数有多少个,仿照归并排序求逆序对的方法,对于两个有序的数组 a , b a,b a,b我们可以这样划分方案,对于 a a a中的每个位置 i i i b b b中有多少个位置与之合法,当 i − > i + 1 i->i+1 i>i+1时相当于 s l s_l sl增大,因此之前成立的 b b b中的位置依旧成立因此类似于双指针的感觉我们可以 O ( n ) O(n) O(n)的维护每个区间的贡献,复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
LL n, m, k, t;
LL a[N];
LL res;
void merge(int l, int r) {
    if (l >= r) return ;
    int mid = l + r >> 1;
    merge(l, mid), merge(mid + 1, r);    
    for (int i = l, j = mid + 1; i <= mid; i ++) {
        while (j <= r && a[j] - a[i] < t) j ++;
        res += j - mid - 1;
    }
    inplace_merge(a + l, a + mid + 1, a + r + 1);
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n >> t;
    for (int i = 1; i <= n; i ++) cin >> a[i], a[i] += a[i - 1];
    merge(0, n);

    cout << res << '\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值