*题解:CF1762F Good Pairs

题目链接

解析

绝对值不是很好处理,我们猜想随着 \(j\) 的增加,\(a_{i_{j}}\) 的变化是单调的。手玩一下可以发现这是对的。于是拆成两个子问题,一个是变换过程中单调不减,一个是单调不增。下文中只讨论单调不减的情况,单调不增的情况同理。

有一个很自然的假想法:从后往前处理,每个元素 \(x\) 向其右侧第一个比它大且差值 \(\le k\) 的元素 \(y\) 连边,这样就会形成若干条链,以某元素为 \(l\) 的合法 \(r\) 个数即为链上在其右边的点的个数。这个过程也可认为是 DP。

上述做法的问题在于没有考虑到更右侧但不在链上的元素可能也会带来贡献。这些元素不在链上是因为 \(< y\),能做贡献是因为 \(\ge x\),且满足这些条件的元素必定做贡献,所以只需统计右侧满足 \(x\le z < y\) 的 \(z\) 的个数,可以使用权值树状数组来统计。至于找 \(y\),可以从大到小扫值域,再用 set 维护当前在范围内的元素编号,利用 lower_bound 找到第一个合法位置即可。

至于单调不增的情况,有个技巧是将原序列翻转再做一遍。

对于相同元素,其贡献(不算 \(l = r\))为 \(\dfrac{t_i(t_i - 1)}{2}\),其中 \(t_i\) 表示数 \(i\) 的出现次数。它会被算两遍,故要减掉一次。

时间复杂度 \(O(n \log n)\)。

没想到的地方

统计右侧第一个差值小于等于 \(k\) 的元素的右侧元素的方法。因为没有注意到这些元素的值域,还想着利用链来整体算贡献。

总结

贡献一起算不了就单独算。

代码

/*
以下为自主做题时的思路: 
a[i[j]] 变化过程单调
考虑给定区间怎么做 
从 l 开始找后面第一个比它大的且大不超过 k 的数,看能否到达 r 
全局来看就形成了若干条链,每个点都在一条链上。
要求 l 开始能到达多少个比他大的 r 就相当于求链上右侧有多少个点。 
小于的情况同理

问题变为判断一个 l 接到哪个链的链头前,在存储每个区间最值的线段树上二分即可 

很可惜这个是错的
对于每个位置,比它大且不超过 k 的数可能有多个,且可能更近的比更远的那个大,此时就统计不到更远的那个了
*/
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int ull;
typedef pair<ull,int> pui;
const int N = 5e5 + 5,M = 1e5 + 5,base = 13331,mod = 1e9 + 7;
vector<int> pos[M],v;
int b[N],cnt[M],r[N],f[N];
int n,k;
int a[N];
ll res;
int ask(int x){
	int res = 0;
	for(;x;x -= x & -x){
		res += b[x];
	}
	return res;
}
void add(int x,int k){
	for(;x < N;x += x & -x){
		b[x] += k;
	}	
}
void sol(){
	set<int> s;
	int j = v.size() - 1;
	for(int i=v.size() - 1;i>=0;i--){
		while(v[j] > v[i] + k){
			for(int p : pos[v[j]]){
				s.erase(p);
			}
			j--;
		}
		for(int k=(int)pos[v[i]].size() - 1;k >= 0;k--){
			int p = pos[v[i]][k];
			auto it = s.lower_bound(p);
			r[p] = it == s.end() ? 0 : *it;
			s.insert(p);
		}
	}
	for(int i=n;i>=1;i--){
		f[i] = f[r[i]];
		if(r[i])
			res += (f[i] += ask(a[r[i]] - 1) - ask(a[i] - 1) + 1);
		add(a[i],1);
	}
	for(int i=1;i<=n;i++){
		add(a[i],-1);
	}
}


int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
//	freopen("in.txt","r",stdin); 
//	freopen("out.txt","w",stderr);
	int T;
	cin>>T;
	while(T--){
		cin>>n>>k;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			v.push_back(a[i]);
			pos[a[i]].push_back(i);
			cnt[a[i]]++;
		}
		res = n;
		sort(v.begin(),v.end());
		v.erase(unique(v.begin(),v.end()),v.end());
		sol();
		reverse(a + 1,a + n + 1);
		for(int i : v){
			pos[i].clear();
		}
		for(int i=1;i<=n;i++){
			pos[a[i]].push_back(i);
		}
		sol();
		for(int i=0;i<v.size();i++){
			res -= 1ll * cnt[v[i]] * (cnt[v[i]] - 1) / 2;
		}
		cout<<res<<'\n';
		for(int i=0;i<v.size();i++){
			pos[v[i]].clear();
			cnt[v[i]] = 0;
		}
		v.clear();
	}
	return 0;
}
posted @ 2026-10-20 19:32  yutar  阅读(4)  评论(0)    收藏  举报