CF1034D Intervals of Intervals

Link

应该是第一道 *3500

将端点化为线段,以便于处理

先考虑 [l,r] 的区间并怎么快速求出。

如果固定 r,考虑维护数轴上每个单位线段最后一次被哪个区间覆盖,记为 lst,则一个询问 [l,r] 的贡献是所有 lst 不小于 l 的单位线段长度之和。用 set 维护覆盖情况,每次的更新情况是区间加(即在区间 (lst,r] 加上大小为长度的贡献)。复杂度 O(nlogn)

二分第 k 个区间的价值,考虑如何求出价值 mid 的区间个数。
考虑到 llrrv(l,r)v(l,r),设 f(i) 表示以 i 为右端点时第一个价值 <mid 的位置,则 f(i) 单调不降。因此用双指针维护答案,用线段树算贡献,复杂度为 O(nlognlogk),不能通过。

考虑在二分前预处理出贡献。贡献形式为 (l,r,v) 表示当询问区间 [L,R] 满足 lLr,Rr 时,会对答案产生 v 的贡献。此时左端点限制仍然难以处理。

进一步差分为 (l,r,v)(r+1,r,v),此时重新定义贡献为当 Ll,Rr 时,询问加上 v
由于双指针的性质 ,L,R 都单调不降,因此贡献一旦加入就不会被撤销。
若将将 [L,R] 看作一个平面点 (L,R),则其贡献为左下角点的权值和。
因此,可以按如下方法维护贡献:当 L 更新时,加入以新的 L 为横坐标,纵坐标小于等于 R 的贡献,对于 R 同理。
对于区间价值和,可以在 R 移动时加上修改的贡献,在 L 移动时直接将该区间的贡献加入即可。

这样,复杂度降为 O(nlogk),可以通过。

一些细节:贡献等于 mid 的点可能有很多,不能直接将 mid 的区间全部求和。

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <vector>
#include <set>
using namespace std;
char buf[1<<14],*p1=buf,*p2=buf;
#define GetC() ((p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++)
struct Ios{}io;
template <typename _tp>
Ios &operator >>(Ios &in,_tp &x){
	x=0;int w=0;char c=GetC();
	for(;!isdigit(c);w|=c=='-',c=GetC());
	for(;isdigit(c);x=x*10+(c^'0'),c=GetC());
	if(w) x=-x;
	return in;
}
const int N=3e5+5;
struct Interval{int l,r,lst;};
bool operator <(Interval a,Interval b){
	if(a.l!=b.l) return a.l<b.l;
	return a.r<b.r;
}
set<Interval> s;
using pii=pair<int,int>;
using ll=long long;
#define pb push_back
void split(int x){
	if(x==0) return ;
	auto pos=--s.upper_bound({x,(int)2e9,0});
	if(pos->r==x) return ;
	s.insert({pos->l,x,pos->lst});
	s.insert({x+1,pos->r,pos->lst});
	s.erase(pos);
}
vector<pii> tagL[N],tagR[N];
ll sum=0,cnt=0;
int n,k;
bool check(int mid){
	sum=cnt=0;
	ll cur=0,cur_sum=0;
	for(int l=1,r=1;r<=n;++r){
		for(auto tmp:tagR[r]){
			if(tmp.first>l) break;
			cur+=tmp.second;
			cur_sum+=(ll)tmp.second*(l-tmp.first);
		}
		while(cur>=mid){
			++l;cur_sum+=cur;
			for(auto tmp:tagL[l]){
				if(tmp.first>r) break;
				cur+=tmp.second;
			}
		}
		sum+=cur_sum;
		cnt+=l-1;
	}
	return cnt>=k;
}
int main(){
	io>>n>>k;
	s.insert({1,(int)1e9,0});
	for(int i=1;i<=n;++i){
		int l,r;io>>l>>r;--r;
		split(l-1),split(r);
		while(1){
			auto pos=s.lower_bound({l,0,0});
			if(pos==s.end()||pos->l>r) break;
			int lst=pos->lst,len=pos->r-pos->l+1;
			tagL[lst+1].pb({i,len});
			tagL[i+1].pb({i,-len});
			tagR[i].pb({lst+1,len});
			tagR[i].pb({i+1,-len});
			s.erase(pos);
		}
		s.insert({l,r,i});
		sort(tagR[i].begin(),tagR[i].end());
	}
	for(int i=1;i<=n;++i) sort(tagL[i].begin(),tagL[i].end());
	int l=1,r=1e9;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) l=mid+1;
		else r=mid-1;
	}
	check(r);
	printf("%lld\n",sum+(k-cnt)*r);
	return 0;
}
posted @   pref_ctrl27  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示