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;
}

浙公网安备 33010602011771号