题解:CF1762F Good Pairs
按值域分块、DP、线段树。
题意
给定一个长为
- 包含
a_l 和a_r ; - 任意相邻两个数差的绝对值不超过
k 。
请求出所有合法的子区间数量。
思路
不妨设
直接 DP 方案书会算重,考虑按值域分块。记
只考虑
看一眼 DP 转移。对于
设右端点为
特判
共
再说一下多测的问题。清空的时候只对当前修改过的元素清空。对于线段树来说,可以写动态开点空间换时间,也可以写版本号树,维护版本号
::::success[代码]
#include<iostream>
#include<vector>
using namespace std;
const int N=5e5+10;
int T,n,k,buc[N];
long long ans,a[N],f[N][3],maxa;
vector<int>use;
struct segmenttree
{
int maxx[N*4],ver[N*4],timee=0;
#define ls (u*2)
#define rs (u*2+1)
void pushup(int u)
{
maxx[u]=max(ver[ls]==timee?maxx[ls]:0,ver[rs]==timee?maxx[rs]:0);
ver[u]=timee;
return;
}
void update(int u,int l,int r,int ql,int qr,int x)
{
if(ql<=l&&r<=qr)
{
maxx[u]=x;
ver[u]=timee;
return;
}
int mid=l+(r-l)/2;
if(ql<=mid)update(ls,l,mid,ql,qr,x);
if(mid<qr)update(rs,mid+1,r,ql,qr,x);
pushup(u);
return;
}
int query(int u,int l,int r,int ql,int qr)
{
if(ver[u]!=timee)return 0;
if(ql<=l&&r<=qr)return maxx[u];
int mid=l+(r-l)/2,res=0;
if(ql<=mid)res=max(res,query(ls,l,mid,ql,qr));
if(mid<qr)res=max(res,query(rs,mid+1,r,ql,qr));
return res;
}
} t;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>T;
while(T--)
{
cin>>n>>k;
maxa=ans=0;
++t.timee;
for(int i=1; i<=n; ++i)cin>>a[i],maxa=max(maxa,a[i]);
if(!k)
{
for(int i=1; i<=n; ++i)
{
if(!buc[a[i]])use.push_back(a[i]);
++buc[a[i]];
}
for(int i:use)ans+=1ll*buc[i]*(buc[i]+1)/2;
for(int i:use)buc[i]=0;
use.clear();
cout<<ans<<'\n';
continue;
}
for(int i=1,j; i<=n; ++i)
{
if(!buc[a[i]/k])use.push_back(a[i]/k);
++buc[a[i]/k];
f[i][0]=1ll*buc[a[i]/k];
j=0;
if(max(1ll,a[i]-k)<=a[i]-1)j=t.query(1,1,maxa,max(1ll,a[i]-k),a[i]-1);
if(j)
{
if(a[i]/k==a[j]/k)f[i][1]=f[i][0]+f[j][1]-f[j][0];
else f[i][1]=f[i][0]+f[j][1];
}
else f[i][1]=f[i][0];
j=0;
if(a[i]+1<=min(maxa,a[i]+k))j=t.query(1,1,maxa,a[i]+1,min(maxa,a[i]+k));
if(j)
{
if(a[i]/k==a[j]/k)f[i][2]=f[i][0]+f[j][2]-f[j][0];
else f[i][2]=f[i][0]+f[j][2];
}
else f[i][2]=f[i][0];
ans+=f[i][1]+f[i][2]-f[i][0];
t.update(1,1,maxa,a[i],a[i],i);
}
for(int i:use)buc[i]=0;
use.clear();
cout<<ans<<'\n';
}
return 0;
}
::::