一、题目
二、解法
(k) 很大还是考虑二分答案,主要是如何检查。
区间覆盖问题可以考虑染色,一开始所有点都是白色间代表第 (i) 种颜色,后染的颜色会覆盖先染的颜色。染色问题有一个很好的性质:每个点上的颜色都是最后覆盖它的颜色。
根据这个性质我们可以考虑对于固定的右端点,维护出所有左端点的答案,假设现在右端点移动到了 (r),在插入区间 (r) 时,我们考虑它替换了颜色 (x) 的某一段(长度为 (len)),那么对于 (lin(x,r]) 的答案会增加 (len),因为在未插入 (r) 的时候这一段都是白色。
假设现在的左端点是 (L),我们要维护出左端点取 ([1,L]) 时的价值和,令 (xleftarrow x+1),如果 (x>L),那么暂时不会造成影响,可以打标记解决;如果 (xleq L),那么会有 ((L-x+1)cdot len) 的权值变化,所以这一部分可以 (O(n)) 解决。
染色的过程需要用 ( t set) 维护,是 (O(nlog n)) 的,但是由于每次检查染色的方式都是一样的,所以我们可以预处理染色,把染色的变化用 ( t vector) 存下来,那么检查就可以做到 (O(n)) 了。
最后具体讲一下怎么用 ( t set) 维护染色,据说这个叫珂朵莉树,我们首先把要加入的区间添加断点(也就是 ( t lower\_bound) 到包含端点的区间之后把区间按照端点断开),然后把端点中的区间删去,再插入新的区间即可。
三、总结
染色是处理区间问题的重要手段,可以用 ( t 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
",res-ans*(num-k));
}