CF1762F Good Pairs 题解
半小时切了。降蓝!()
先固定
我们维护一个区间
[x,y] ,初始为[a_r,a_r] 。扫描i = r \to 1 。
- 如果扫到
a_i 时有a_i > y + k ,则a_i 不合法。否则把y 替换成\max(y,a_i) 。- 如果扫到
a_i 时有a_i < x - k ,则a_i 不合法。否则把x 替换成\min(x,a_i) 。相当于,我们记录一个合法区间
[x,y] ,倒着扫,每次贪心地尝试扩张这个区间。显然
y 是我们最大的合法值,x 是最小的合法值,在这个区间外的都不合法,反之,区间内的值都可以通过调整来合法。
由于
合法的
对于
在
其中
需要在线维护
代码实现时,由于两颗线段树都是值域上的,可以干脆合并成一颗。空间要开的极限一点。
此外还有一个细节,不能
::::info[Code / 1.4s 230MB]
#include<bits/stdc++.h>
using namespace std;
#define N 500009
int n,m,k,qwq,T,V,rt[N],f[N],g[N],a[N];
struct seg{ int l,r,ls,rs,mx,cnt; } tr[N * 19];
void build(int &u,int l,int r){
u = ++V; tr[u].l = l; tr[u].r = r;
if(l == r) return;
int mid = (l + r) >> 1;
build(tr[u].ls,l,mid); build(tr[u].rs,mid + 1,r);
}
void upd(int &u,int x,int c){
tr[++V] = tr[u]; u = V;
if(tr[u].l == tr[u].r){ tr[u].cnt++; tr[u].mx = c; return; }
if(x <= tr[tr[u].ls].r) upd(tr[u].ls,x,c);
else upd(tr[u].rs,x,c);
tr[u].mx = max(tr[tr[u].ls].mx,tr[tr[u].rs].mx);
tr[u].cnt = tr[tr[u].ls].cnt + tr[tr[u].rs].cnt;
}
int qmx(int u,int l,int r){
if(l <= tr[u].l && tr[u].r <= r) return tr[u].mx;
if(r < tr[u].l || tr[u].r < l) return 0;
return max(qmx(tr[u].ls,l,r),qmx(tr[u].rs,l,r));
}
int qcnt(int u,int l,int r){
if(l <= tr[u].l && tr[u].r <= r) return tr[u].cnt;
if(r < tr[u].l || tr[u].r < l) return 0;
return qcnt(tr[u].ls,l,r) + qcnt(tr[u].rs,l,r);
}
signed main(){
cin.tie(0); cout.tie(0);
ios::sync_with_stdio(0);
m = 100000; build(rt[0],1,m); qwq = V;
cin>>T;
while(T--){
cin>>n>>k;
for(int i = 1;i <= n;i++) cin>>a[i];
V = qwq;
long long ans = 1ll * n * (n + 1) / 2;
for(int i = 1;i <= n;i++){
int x = qmx(rt[i - 1],a[i],min(m,a[i] + k));
f[i] = f[x] + qcnt(rt[i - 1],a[i] + k + 1,m) - qcnt(rt[x],a[i] + k + 1,m);
int y = qmx(rt[i - 1],max(1,a[i] - k),a[i]);
g[i] = g[y] + qcnt(rt[i - 1],1,a[i] - k - 1) - qcnt(rt[y],1,a[i] - k - 1);
rt[i] = rt[i - 1]; upd(rt[i],a[i],i);
ans -= f[i] + g[i];
// cout<<"! "<<x<<" "<<y<<" "<<f[i]<<" "<<g[i]<<endl;
}
cout<<ans<<endl;
}
}
::::