CF1034D Intervals of Intervals

题意:给定 \(N\) 条线段,定义一个区间的价值为区间线段并的长度,求前 \(K\) 大区间价值和。

题解:首先考虑一个简化版本,求区间线段并。

扫描线,维护每个左端点的答案。对于每个位置维护最后一次包含它的线段,把相邻相同的位置合并,用 \(\texttt{set}\) 维护这些连续段,复杂度是均摊的。

回到原题,考虑二分出 \(K\) 大值。令 \(L_i\) 表示以 \(i\) 为右端点时区间并 \(<M\) 的左端点,显然 \(L_i\) 单调不降。双指针的同时也可以维护出答案,详见代码。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
const int mod=1e9+7;
const int inf=1e9;
#define pb push_back
#define pii pair<int,int>
#define ll long long
#define lll __int128
#define fi first
#define se second
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
struct itv{
	int l,r,tg;
	bool operator<(const itv &x)const{
		return x.l^l?l<x.l:r<x.r;
	}
};set<itv>S;
inline void split(int p){
	if(!p)return;
	auto it=--S.lower_bound({p+1,0,0});
	if(it->r==p)return;
	S.insert({it->l,p,it->tg});
	S.insert({p+1,it->r,it->tg});
	S.erase(it);
}
int n,m;lll sum,cnt;
vector<pii>laz[maxn],buc[maxn];
inline bool chk(int mid){
	sum=cnt=0;ll cs=0,cur=0;
	for(int l=1,r=1;r<=n;r++){
		for(auto x:laz[r])if(x.fi<=l)
			cur+=x.se,cs+=1ll*x.se*(l-x.fi);
		while(cur>=mid){
			cs+=cur,++l;
			for(auto x:buc[l])
				if(x.fi<=r)cur+=x.se;
		}sum+=cs,cnt+=l-1;
	}return cnt>=m;
}
int main(){
	n=read(),m=read();
	S.insert({1,inf,0});
	for(int i=1,l,r;i<=n;i++){
		l=read(),r=read()-1;
		split(l-1),split(r);
		while(1){
			auto it=S.lower_bound({l,0,0});
			if(it==S.end()||it->l>r)break;
			int las=it->tg,len=it->r-it->l+1;
			laz[i].pb({las+1,len});
			laz[i].pb({i+1,-len});
			buc[las+1].pb({i,len});
			buc[i+1].pb({i,-len});
			S.erase(it); 
		}S.insert({l,r,i});
	}int l=1,r=inf,kth=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(chk(mid))kth=mid,l=mid+1;
		else r=mid-1;
	}chk(kth);
	printf("%lld\n",(ll)(sum-(cnt-m)*kth));
	return 0;
}
posted @ 2022-11-21 02:32  syzf2222  阅读(29)  评论(0编辑  收藏  举报