CF1034D Intervals of Intervals

\(\mathcal Link\)

应该是第一道 *3500

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

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

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

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

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

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

这样,复杂度降为 \(\mathcal O(n\log k)\),可以通过。

一些细节:贡献等于 \(mid\) 的点可能有很多,不能直接将 \(\geq 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 @ 2022-11-21 15:42  pref_ctrl27  阅读(14)  评论(0编辑  收藏  举报