把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF1762F Good Pairs 分析

题目概述

求有多少个 \((l,r)(l\leq r)\),存在一组 \((i_1,i_2,\dots,i_m)\) 满足:

  • 对于任意 \(j\in[2,m]\)\(i_j>i_{j-1}\)
  • \(i_1=l,i_m=r\)
  • \(|a_{i_j}-a_{i_{j-1}}|\leq k\)

数据范围:\(1\leq n\leq 5\times 10^5,1\leq a_i\leq 10^5\)

分析

注意到 \(|x|\) 并不好求,我们可以优先考虑只递增的情况。

那么贪心是不行的,因为有可能当前所能达到的不一定全部都能达到。

于是我们设 \(f_i\) 表示我的 \(l=i\),有多少个 \(r\) 满足条件。

显然答案是 \(\sum f_i\)

怎么转移呢?

因为我们考虑的是后面的值,所以倒着枚举 \(i\)

那么我们为了去转移到更多满足条件的 \(r\) 显然是找到一个最近的 \(j\) 满足 \(a_j\in[a_i+1,a_i+k]\)

这样 \(l=j\) 能到的,我 \(l=i\) 一定能到。而且由于是递增的,所以说我找的 \(l=j\) 所有的 \(r\) 一定涵盖于 \(>j\) 的。

但是我们还需要一些 \(j\) 到不了的,那么显然就是 \(a_x\in[a_i+1,a_j]\),用一个树状数组统计就行了。

于是:

\[f_i=f_j+cnt \]

其中 \(cnt\) 表示后面满足 \(a_x\in[a_i+1,a_j]\)\(x\) 的数量。

于是我们递增的情况就做完了。

考虑原题,如果先下降再递增,那么下降后想要递增到的那个点我一开始就能到达,如果现递增再下降也是一样,所以一定是一直递增或者一直下降

这个只需要翻转一下序列再做一遍就行了,最后再处理一下相等的情况,这道题目就做完了。

思维好题!

代码

时间复杂度 \(\mathcal{O}(\sum n\log n)\)

#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <cstdio>
#include <algorithm>
#include <vector>
#define int long long
#define N 500005
using namespace std;
int tr[N];
void update(int x,int val,int n) {
    for (;x <= n;x += x & -x) tr[x] += val;
}
int query(int x) {
    int res = 0;
    for (;x;x -= x & -x) res += tr[x];
    return res;
}
int querysum(int l,int r) {
    return query(r) - query(l - 1);
}
struct seg{
    int tr[N << 2];
    #define ls(x) (x << 1)
    #define rs(x) (x << 1 | 1)
    void pushup(int x) {
        tr[x] = min(tr[ls(x)],tr[rs(x)]);
    }
    void build(int x,int l,int r) {
        tr[x] = 1e9;
        if (l == r) return;
        int mid = l + r >> 1;
        build(ls(x),l,mid),build(rs(x),mid + 1,r);
    }
    void clear(int x,int l,int r) {
        if (tr[x] == 1e9) return;
        tr[x] = 1e9;
        if (l == r) return;
        int mid = l + r >> 1;
        clear(ls(x),l,mid),clear(rs(x),mid + 1,r);
    }
    void update(int x,int l,int r,int pos,int val) {
        if (l == r) {
            tr[x] = min(tr[x],val);
            return;
        }
        int mid = l + r >> 1;
        if (pos <= mid) update(ls(x),l,mid,pos,val);
        else update(rs(x),mid + 1,r,pos,val);
        pushup(x);
    }
    int query(int x,int l,int r,int L,int R) {
        if (l > R || r < L) return 1e9;
        if (L <= l && r <= R) return tr[x];
        int mid = l + r >> 1;
        return min(query(ls(x),l,mid,L,R),query(rs(x),mid + 1,r,L,R));
    }
}t;
int T,n,k,a[N],f[N],g[N],cnt[N];
signed main(){
    cin >> T;
    t.build(1,1,1e5);
    for (;T--;) {
        int ans = 0;
        scanf("%lld%lld",&n,&k);
        int mx = 0;
        for (int i = 1;i <= n;i ++) scanf("%lld",&a[i]),mx = max(mx,a[i]),cnt[a[i]] ++,ans += cnt[a[i]],f[i] = 0;
        for (int i = n;i;i --) {
            int j = t.query(1,1,mx,a[i] + 1,min(mx,a[i] + k));
            if (j != 1e9) f[i] = f[j] + querysum(a[i] + 1,a[j]);
            update(a[i],1,mx);
            t.update(1,1,mx,a[i],i);
        }
        for (int i = 1;i <= n;i ++) ans += f[i],update(a[i],-1,mx),f[i] = 0;
        t.clear(1,1,mx);
        reverse(a + 1,a + 1 + n);
        for (int i = n;i;i --) {
            int j = t.query(1,1,mx,a[i] + 1,min(mx,a[i] + k));
            if (j != 1e9) f[i] = f[j] + querysum(a[i] + 1,a[j]);
            update(a[i],1,mx);
            t.update(1,1,mx,a[i],i);
        }
        for (int i = 1;i <= n;i ++) ans += f[i],update(a[i],-1,mx),f[i] = 0,cnt[a[i]] --;
        printf("%lld\n",ans);
        t.clear(1,1,mx);
        // cout << ans << 'f' << '\n';
    }
    return 0;
}
posted @ 2025-10-30 15:41  high_skyy  阅读(6)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end