CF1034D Intervals of Intervals

一、题目

点此看题

二、解法

\(k\) 很大还是考虑二分答案,主要是如何检查。

区间覆盖问题可以考虑染色,一开始所有点都是白色间代表第 \(i\) 种颜色,后染的颜色会覆盖先染的颜色。染色问题有一个很好的性质:每个点上的颜色都是最后覆盖它的颜色

根据这个性质我们可以考虑对于固定的右端点,维护出所有左端点的答案,假设现在右端点移动到了 \(r\),在插入区间 \(r\) 时,我们考虑它替换了颜色 \(x\) 的某一段(长度为 \(len\)),那么对于 \(l\in(x,r]\) 的答案会增加 \(len\),因为在未插入 \(r\) 的时候这一段都是白色。

假设现在的左端点是 \(L\),我们要维护出左端点取 \([1,L]\) 时的价值和,令 \(x\leftarrow x+1\),如果 \(x>L\),那么暂时不会造成影响,可以打标记解决;如果 \(x\leq L\),那么会有 \((L-x+1)\cdot len\) 的权值变化,所以这一部分可以 \(O(n)\) 解决。

染色的过程需要用 \(\tt set\) 维护,是 \(O(n\log n)\) 的,但是由于每次检查染色的方式都是一样的,所以我们可以预处理染色,把染色的变化用 \(\tt vector\) 存下来,那么检查就可以做到 \(O(n)\) 了。

最后具体讲一下怎么用 \(\tt set\) 维护染色,据说这个叫珂朵莉树,我们首先把要加入的区间添加断点(也就是 \(\tt lower\_bound\) 到包含端点的区间之后把区间按照端点断开),然后把端点中的区间删去,再插入新的区间即可。

三、总结

染色是处理区间问题的重要手段,可以用 \(\tt set\) 维护染色过程。

一段区间产生贡献的题,可以固定右端点,维护出所有左端点的答案。

#include <cstdio>
#include <vector>
#include <iostream>
#include <set>
using namespace std;
const int M = 300005;
const int inf = 1e9;
#define sit set<node>::iterator
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,k,ans,num,res,ad[M];
struct node
{
	int l,r,t;
	bool operator < (const node &b) const
	{
		if(l==b.l) return r<b.r;
		return l<b.l;
	}
};set<node> s;vector<node> g[M];
sit get(int p)
{
	sit it=s.lower_bound(node{p,0,0});
	if(it!=s.end() && (*it).l==p) return it;
	it--;
	if((*it).r==p) return s.end();
	node t=*it;
	s.erase(it);
	s.insert(node{t.l,p,t.t});
	return s.insert(node{p,t.r,t.t}).first;
}
int check(int mid)
{
	num=res=0;int val=0,sum=0;
	for(int i=1;i<=n;i++) ad[i]=0;
	for(int i=1,L=0;i<=n;i++)
	{
		for(int j=0;j<g[i].size();j++)
		{
			if(g[i][j].l>L)
				ad[g[i][j].l]+=g[i][j].r;
			else
				val+=g[i][j].r,
				sum+=g[i][j].r*(L-g[i][j].l+1);
			ad[i+1]-=g[i][j].r;
		}
		for(;L<i && val+ad[L+1]>=mid;L++)
			val+=ad[L+1],sum+=val;
		num+=L;res+=sum;
	}
	return num;
}
signed main()
{
	n=read();k=read();
	s.insert(node{1,inf,0});
	for(int i=1;i<=n;i++)
	{
		int a=read(),b=read();
		sit r=get(b),l=get(a);
		for(;l!=r;)
		{
			node t=*l;
			s.erase(l++);
			//attention OPEN interval
			g[i].push_back(node{t.t+1,t.r-t.l,0});
		}
		s.insert(node{a,b,i});
	}
	int l=1,r=inf;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		check(mid);
		if(num>=k)
			ans=mid,l=mid+1;
		else
			r=mid-1;
	}
	printf("%lld\n",res-ans*(num-k));
}
posted @ 2021-07-13 20:30  C202044zxy  阅读(138)  评论(0编辑  收藏  举报