题目描述:
题目分析:
先考虑如何计算一个“区间的区间”的权值。
把区间按照
1
1
1 到
n
n
n 的顺序依次加进来,维护每个位置最后被覆盖的时间。
对于“区间的区间”
[
l
,
r
]
[l,r]
[l,r],它的权值就是加入
1
1
1到
r
r
r后,最后覆盖时间
≥
l
\ge l
≥l的位置的长度之和。
维护被覆盖的最后时间有个经典的方法:把覆盖时间相同的位置合并成一段,然后用set维护它们,每次只会添加至多两个段,所以段的变化次数是
O
(
n
)
O(n)
O(n)的。其实就是复杂度有保证的ODT(珂朵莉树)。
当一段位置的覆盖时间由
x
x
x变为
y
y
y时,在
[
x
+
1
,
y
]
[x+1,y]
[x+1,y]之间的
l
l
l对应的权值都会变大。
如果要查询一个区间的区间的权值,用线段树维护即可。
回到原问题,要求前
k
k
k 大的“区间的区间”的和,一个想法是二分一个权值
x
x
x,然后看所有权值
≥
x
\ge x
≥x的“区间的区间”的个数是否够
k
k
k 个,并统计它们的和。
二分完之后取的区间个数可能大于k个,后面一截的权值都是相等的,答案减去多的个数*二分的权值即可。
现在的问题就是如何求权值
≥
x
\ge x
≥x的“区间的区间”个数以及它们的和,按照最开始的想法,对于每个
r
r
r,在线段树上二分出最大的
l
l
l就可以统计答案了,这样做的复杂度是
O
(
n
log
n
(
log
n
+
log
max
b
i
)
)
O(n\log n(\log n+\log\max b_i))
O(nlogn(logn+logmaxbi)),常数较大,可能过不了。
注意到随着
r
r
r 的增大,
l
l
l 是单调不降的,于是可以不用线段树,维护当前最大的
l
l
l 以及它的权值,区间修改时在差分数组上打标记(然后根据
l
l
l与差分位置的关系修改权值),
r
r
r 增大时尝试增大最大的
l
l
l的值。
区间修改操作可以预处理后存放在每个 r r r对应的vector中,不必每次二分重新求。
Code:
#include<bits/stdc++.h>
#define maxn 300005
#define LL long long
using namespace std;
typedef pair<LL,LL> pll;
typedef pair<int,int> pii;
int n,K,add[maxn];
vector<pii>q[maxn];
struct node{
int l,r,t;
node(int l=0,int r=0,int t=0):l(l),r(r),t(t){}
bool operator < (const node &p)const{return l<p.l;}
};
set<node>S;
typedef set<node>::iterator IT;
IT split(int x){//cut to be x][x
IT it = S.lower_bound(x);
if(it!=S.end()&&(*it).l==x) return it;
node p=*--it;
S.erase(it),S.insert(node(p.l,x,p.t));
return S.insert(node(x,p.r,p.t)).first;
}
pll check(int mid){
pll ret;
LL sum=0; int L=0,val=0;
for(int i=1;i<=n;i++){
for(pii D:q[i]){
if(D.first>L) add[D.first]+=D.second;
else val+=D.second,sum+=1ll*D.second*(L-D.first+1);
add[i+1]-=D.second;
}
for(;L<i&&val+add[L+1]>=mid;L++) val+=add[L+1],sum+=val;
ret.first+=L,ret.second+=sum;
}
memset(add,0,(n+1)<<2);
return ret;
}
int main()
{
scanf("%d%d",&n,&K);
S.insert(node(1,1e9));
for(int i=1,a,b;i<=n;i++){
scanf("%d%d",&a,&b);
IT r=split(b),l=split(a);
for(IT it=l;it!=r;++it) {node p=*it; q[i].push_back(make_pair(p.t+1,p.r-p.l));}
S.erase(l,r),S.insert(node(a,b,i));
}
int l=1,r=1e9,mid;
while(l<r) mid=(l+r+1)>>1,check(mid).first>=K?(l=mid):(r=mid-1);
pll ans=check(l);
printf("%lld\n",ans.second-(ans.first-K)*l);
}