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));
}