题意:
给
n
n
条带权线段 , 要求选
m
m
个点, 使得所有 包含这个点中任意一个的区间的和最大。
题解:
记 Ans(i) A n s ( i ) 为 i i 个点的答案, 那么有。
直接计算最优值的话, 显然 i=n i = n 时取得最优值, 因为上凸,我们可以让选择每个点付出额外的 λ λ 代价。 当取最优值的点的个数为 m m 时这个方案就是最终答案,我们二分这个代价即可。
考虑如何计算最优值:
令表示只考虑右端点不超过
i
i
的区间的最优值。
于是暴力dp的转移式为。
其中 val(i,j) v a l ( i , j ) 表示 ls≤k≤rs≤i l s ≤ k ≤ r s ≤ i 的区间价值和。
发现其实是支持后缀加入一个数,后缀加,求最大值。
可以用线段树维护, 不过有常数更小的并查集做法。
直接差分原数组, 大于0的就往左合并即可,时间复杂度
O(nlognlogX)
O
(
n
log
n
log
X
)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <int,int> pii;
const int RLEN=1<<18|1;
inline char nc() {
static char ibuf[RLEN],*ib,*ob;
(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
char ch=nc(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
return i*f;
}
const int N=5e5+50;
int n,m,tot;
vector <pii> e[N];
int r[N],l[N],s[N],c[N],f[N];
inline int ga(int x) {return (r[x]==x) ? x : (r[x]=ga(r[x]));}
inline void fix(int p) {
while(~l[p] && s[p]>=0) {
if(!s[p]) c[p]=max(c[p],c[l[p]]);
r[l[p]]=p; s[p]+=s[l[p]]; l[p]=l[l[p]];
}
}
inline pii check(int v) {
for(int i=0;i<=n;i++) r[i]=i, l[i]=i-1, s[i]=0, c[i]=0;
s[0]=v; s[1]=-v; c[0]=-1;
for(int i=1;i<=n;i++) {
int u=ga(0); f[i]=s[u]-v; c[i]=(c[u]+1);
s[i]+=f[i]-f[i-1]; fix(i);
for(int p=e[i].size()-1;p>=0;p--) {
u=e[i][p].first; int w=e[i][p].second;
u=ga(u); s[u]+=w; fix(u); s[i+1]-=w;
}
}
pii mx=pii(0,0);
for(int i=0;i<=n;i++)
mx=max(mx,pii(f[i],c[i]));
return mx;
}
int main() {
n=rd(), m=rd();
for(int i=1;i<=n;i++) {
int l=rd(), r=rd()-1, c=rd();
tot=max(tot,r+1);
e[r].push_back(pii(l,c));
}
n=tot;
int l=0, r=1e9, ans=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid).second>=m) ans=mid, l=mid+1;
else r=mid-1;
}
cout<<check(ans).first+(LL)m*ans;
}