ABC462D 题解

ABC462D 题解

link.

题目描述

给定 \(n\) 个区间 \([l,r]\),求有多少个 \(s,i,j\) 满足 \([s,s+d]\)\([l_i,r_i]\cap [l_j,r_j]\) 包含,其中 \(1\le i<j\le n\)

数据范围请自己看题目。

思路

首先对所有区间进行排序是显然的。以 \(l\) 为第一关键字,以 \(r\) 为第二关键字从小到大排。

然后我们发现枚举两个区间的时间复杂度是 \(O(n^2)\) 的,直接爆炸。

观察一下发现所有区间左右端点的取值范围才 \(10^6\),所以考虑枚举开始作案的时间 \(s\)

所以我们可以找到最后一个满足 \(l_k<s\) 的区间 \(k\),那么包含 \([s,s+d]\) 的区间一定是从 \(1\sim k\) 里面找。

给张图理解下。

由于我们的区间是按照 \(l\) 从小到大排序了,所以在 \(k\) 之前的区间的 \(l\) 一定也小于 \(s\),也就是一定合法。

现在看怎么找 \(k\)

然后注意到我们已经对所有区间排完序了,包含区间 \([s,s+d]\) 的区间们在编号上一定也是个区间,且是整体的前缀。

而我们也是从小到大找 \(s\) 的,所以我们可以不断判断下一个区间的 \(l\) 是否小于 \(s\),时间复杂度均摊 \(O(1)\),不懂看代码。

接下来在 \(1\sim k\) 中的区间的左端点一定是合法的,所以只要看多少个右端点大等于 \(s+d\) 即可。这个可以用树状数组 \(O(\log n)\) 求。

假设有 \(S\) 个区间的右端点大等于 \(s+d\),那么对答案的贡献就是 \(\binom{S}{2}=\frac{S(S-1)}{2}\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
const int N=2e5+5,M=1e6+5;
int n,cur;
ljl ans,mxr,trc[M],d;
int lowbit(int x){return x&(-x);}
ljl query(int x){
	ljl ans=0;
	for(;x>0;x-=lowbit(x))ans+=trc[x];
	return ans;
}
ljl querylr(ljl l,ljl r){return query(r)-query(l-1);}
void add(int x,ljl val)
{
	for(;x<=mxr;x+=lowbit(x))trc[x]+=val;
	return;
}
struct NODE{
	ljl l,r;
	bool operator < (const NODE a)const{
		if(l!=a.l)return l<a.l;
		return r<a.r;
	}
}node[N];
int main(){
	ios::sync_with_stdio(0);
	cin>>n>>d;
	for(int i=1;i<=n;++i)
	{
		cin>>node[i].l>>node[i].r;
		mxr=max(mxr,node[i].r);
	}
	sort(node+1,node+n+1);
	
	for(ljl st=1;st+d<=mxr;++st)
	{
		while(cur<n&&node[cur+1].l<=st)//这里的cur最多只会增加n次,所以复杂度均摊O(1)
		{
			++cur;
			add(node[cur].r,1ll);
//			cout<<"------\n"; 
		}
		ljl sum=querylr(st+d,mxr);
//		cout<<st<<": "<<sum<<'\n';
		ans=ans+(sum*(sum-1)/2);
	}
	cout<<ans<<'\n';
	return 0;
}

posted @ 2026-06-13 23:03  Atserckcn  阅读(19)  评论(0)    收藏  举报