CF1034D Intervals of Intervals 题解

首先我们可以二分出第\(k\)条线的长度,然后统计长度大于等于这条线的线长度的总和(如果个数多了减去对应个数乘第\(k\)条线的长度即可,原因显然,在此略掉证明)

考虑转化题意,变成一条\(n\)个点,统计每一个点最后一次被哪一条线覆盖,如果说第\(i\)条线覆盖了第\(x\)条线\(l\)\(r\)的位置,就会给\(x+1\)\(i\)增加\(r-l+1\)的权值,然后使用颜色段均摊,可以整出总共需要加的\(O(n)\)段,考虑枚举右端点,左端点单调不降,考虑神秘差分(思路大体为大于\(x\)的位置,然后不断右移统计总个数/总长度。具体见代码,非常的easy)。

代码:

#include <bits/stdc++.h>
#define int long long
#define pii pair<pair<int,int>,int>
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)/2)
using namespace std;
const int maxn=3e5+10;
int n,k,a[maxn],b[maxn],now,ans,anss,ge,wei,l1,l2,l3,L,nowg;
int l4,l5,l[maxn<<2],r[maxn<<2],v[maxn<<2],tot,tp,f[maxn],res,ll,rr;
set<pii>s;
inline void duan(int q){
	auto it=s.lower_bound({{q,0},0});
	if((*it).first.first==q){
		return;
	}
	it--;
	l1=(*it).first.first;
	l2=q-1;
	l3=q;
	l4=(*it).first.second;
	l5=(*it).second;
	s.erase(it);
	s.insert({{l1,l2},l5});
	s.insert({{l3,l4},l5});
	return;
}
void init(){
	s.insert({{1,rr},0});
	s.insert({{rr+1,rr+1},0});
	for(int i=1;i<=n;i++){
		duan(a[i]);
		duan(b[i]+1);
		auto it=s.lower_bound({{a[i],0},0});
		while((*it).first.first<=b[i]){
			tot++;
			l[tot]=(*it).second+1;
			r[tot]=i;
			v[tot]=(*it).first.second-(*it).first.first+1;
			s.erase(it);
			it=s.lower_bound({{a[i],0},0});
		}
		s.insert({{a[i],b[i]},i});
	}
}
inline int check(int jie){
	for(int i=1;i<=n;i++){
		f[i]=0;
	}
	ge=0;
	L=0;
	tp=0;
	for(int i=1;i<=n;i++){
		while(r[tp+1]==i){
			tp++;
			f[max(l[tp],L)]+=v[tp];
			f[r[tp]+1]-=v[tp];
		}
		while(L+1<=i&&f[L+1]+f[L]>=jie){
			f[L+1]+=f[L];
			L++;
		}
		ge+=L;
	}
	return ge;
}
int get_ans(int jie){
	for(int i=1;i<=n;i++){
		f[i]=0;
	}
	L=0;
	tp=0;
	ans=0;
	res=0;
	for(int i=1;i<=n;i++){
		while(r[tp+1]==i){
			tp++;
			if(l[tp]<=L){
				res+=(L-l[tp]+1)*v[tp];
			}
			f[max(l[tp],L)]+=v[tp];
			f[r[tp]+1]-=v[tp];
		}
		while(L+1<=i&&f[L+1]+f[L]>=jie){
			f[L+1]+=f[L];
			res+=f[L+1];
			L++;
		}
		ans+=res;
	}
	return ans;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];
		b[i]--;
		rr=max(rr,b[i]);
	}
	init();
	ll=1;
	while(ll<=rr){
		int midd=(ll+rr)/2;
		if(check(midd)>=k){
			now=midd;
			ll=midd+1;
		}
		else{
			rr=midd-1;
		}
	}
	cout<<get_ans(now)-(check(now)-k)*now;
	return 0;
}
posted @ 2025-04-28 19:06  特别之处  阅读(13)  评论(0)    收藏  举报