专栏文章

题解:CF2042D Recommendations

CF2042D题解参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@mioss4u3
此快照首次捕获于
2025/12/03 00:33
3 个月前
此快照最后确认于
2025/12/03 00:33
3 个月前
查看原文
题意可以被描述为:给定 nn 个区间,求每个区间的母区间的交集的长度再减掉自身的长度。
先按照左端点为第一关键字升序排序。
这样,对于排序后第 ii 个区间,前面的区间中如果有右端点大于 ii 的右端点的区间就是第 ii 个区间的母区间。
贡献有两部分:第 ii 个区间的左边和右边。
但是请注意,不管是哪部分的贡献,都建立在一个前提上:母区间的交集。
第一部分的贡献(左边)怎么求?我们需要找到一个最大的母区间左端点。
第二部分的贡献(右边)怎么求?我们需要找到一个最小的母区间右端点。
这两部分的贡献有多种求法。
  1. 可以线段树二分:先离散化,记录下前缀每个数的数值的前缀和,出现次数的前缀和,这可能需要开两颗线段树。
  2. 树状数组:先离散化,这种做法单次询问要 O(log2n)O(\log^2n),不过树状数组常数小,实测和线段树没什么区别。
  3. 观察到每次查询的是一段前缀,那就可以直接开 set 维护,用 STL 自带的二分单次查询 O(logn)O(\log n)。比较好写。
代码量很不小。
复杂度 O(nlogn+qlogn)O(n\log n+q\log n)
CPP
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+10;
multiset<int> s;
int n,ans[N];
struct str{
	int l,r,id;
}a[N];
struct tee{
	int num,id,minn;
}tree[N<<2];
bool cmp(str a,str b){
	if(a.l == b.l) return a.r > b.r;
	else return a.l < b.l;
}
inline int ls(int p){return p << 1;}
inline int rs(int p){return (p << 1) | 1;}
void Push_up(int p){
	tree[p].num = max(tree[ls(p)].num,tree[rs(p)].num);
	tree[p].minn = min(tree[ls(p)].minn,tree[rs(p)].minn);
}
void Build(int s,int t,int p){
	if(s == t){
		tree[p].minn = a[s].r;
		tree[p].num = a[s].r;
		tree[p].id = a[s].id;
		return;
	}
	int mid = s+((t-s)>>1);
	Build(s,mid,ls(p));
	Build(mid+1,t,rs(p));
	Push_up(p);
}
tee Getnum(int l,int r,int s,int t,int p){
	if(l <= s && t <= r) return tree[p];
	int mid = s+((t-s)>>1);
	tee maxn = {0,0};
	if(l <= mid){
		tee res = Getnum(l,r,s,mid,ls(p));
		if(res.num > maxn.num) maxn = res;
	}
	if(r > mid){
		tee res = Getnum(l,r,mid+1,t,rs(p));
		if(res.num > maxn.num) maxn = res;
	}
	return maxn;
}
int Getmin(int l,int r,int s,int t,int p){
	if(l <= s && t <= r) return tree[p].minn;
	int mid = s+((t-s)>>1),res = 1e9+10;
	if(l <= mid) res = min(res,Getmin(l,r,s,mid,ls(p)));
	if(r > mid)  res = min(res,Getmin(l,r,mid+1,t,rs(p)));
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int T; cin>>T;
	while(T--){
		cin>>n; s.clear();
		for(int i = 1;i<=n+10;i++) ans[i] = 0,a[i].l = a[i].r = a[i].id = 0;
		for(int i = 1;i<=(n<<2)+10;i++)
			tree[i].num = tree[i].id = 0,tree[i].minn = 0;
		for(int i = 1;i<=n;i++){
			cin>>a[i].l>>a[i].r;
			a[i].id = i;
		}
		sort(a+1,a+n+1,cmp);
		Build(1,n,1);
		s.insert(a[1].r);
		for(int i = 2;i<=n;i++){
			if(i < n && a[i].l == a[i+1].l && a[i].r == a[i+1].r){
				s.insert(a[i].r);
				continue;
			}
			int l = 1,r = i-1,tmp = i;
			while(l <= r){
				int mid = l+((r-l)>>1);
				tee res = Getnum(mid,i-1,1,n,1);
				if(res.num >= a[i].r) tmp = mid,l = mid+1;
				else r = mid-1;
			}
			ans[a[i].id] = a[i].l - a[tmp].l;
			auto p = s.lower_bound(a[i].r);
			if(p != s.end()) ans[a[i].id] += *p - a[i].r;
			s.insert(a[i].r);
		}
		for(int i = 1;i<=n;i++)
			cout<<ans[i]<<'\n';
	}

	return 0;
}

评论

1 条评论,欢迎与作者交流。

正在加载评论...