在举办了打字竞赛和大扫除后,学校决定买蛋糕来犒劳学生。
学校派了一些学生去蛋糕店买蛋糕,宫水三叶是其中之一。
蛋糕店总共有 n n n 个蛋糕,学校决定买 m m m 个,并且把它们摆在操场上。
每一个蛋糕有两种价值 v i , c i v_i,c_i vi,ci。假设学校确定了每个蛋糕,那么获得的总价值为:
∑ i = 1 m ( v i − ∣ c i − c i + 1 ∣ ) \sum_{i=1}^{m} (v_i-|c_i-c_{i+1}|) i=1∑m(vi−∣ci−ci+1∣)
其中 c m + 1 = c 1 c_{m+1}=c_{1} cm+1=c1。
现在三叶知道了商店里 n n n 种蛋糕的 v i , c i v_i,c_i vi,ci,她需要确定一种选择 m m m 个蛋糕并把这些蛋糕排序的方案,使得上述价值最大。
形式化的说:给定 n n n 个整数对 v i , c i v_i,c_i vi,ci,你需要选择 m m m 个整数对并按照某种顺序排列,使得 ∑ i = 1 m ( v i − ∣ c i − c i + 1 ∣ ) \sum_{i=1}^{m} (v_i-|c_i-c_{i+1}|) ∑i=1m(vi−∣ci−ci+1∣) 最大。
第一行两个整数 n , m n,m n,m 。
接下来 n n n 行,每一行两个整数 v i , c i v_i,c_i vi,ci 。
一行一个整数,表示答案。
样例输入1
5 3
2 1
4 2
6 4
8 8
10 16
样例输出 1
6
样例解释 1
选择 1 , 3 , 2 1,3,2 1,3,2 。
样例输入 2
8 4
112103441 501365808
659752417 137957977
86280801 257419447
902409188 565237611
965602301 689654312
104535476 646977261
945132881 114821749
198700181 915994879
样例输出 2
2323231661
本题采用捆绑测试。
对于所有数据,满足 3 ≤ m ≤ n ≤ 2 × 1 0 5 , 1 ≤ v i , c i ≤ 1 0 9 3\le m \le n \le 2\times 10^5,1\le v_i,c_i \le 10^9 3≤m≤n≤2×105,1≤vi,ci≤109。
子任务编号 | n n n | 分值 |
---|---|---|
1 1 1 | ≤ 1 0 2 \le 10^2 ≤102 | 5 5 5 |
2 2 2 | ≤ 2 × 1 0 3 \le 2\times 10^3 ≤2×103 | 19 19 19 |
3 3 3 | ≤ 2 × 1 0 5 \le 2\times 10^5 ≤2×105 | 76 76 76 |
题解:
首先,考虑我们已经有一个序列,则必须按照升序排序,贪心易证。
其次,若确定
c
c
c 最大值和最小值,那么
v
v
v 取前
m
m
m 大最优。
这时,我们考虑枚举最小值,发现最优决策的右端点对于左端点是单调递增的。
即
f
[
i
]
f[i]
f[i] 表示以
c
i
c_i
ci 为最小值,使得答案最优的右端点(最大值)。则
i
≤
j
,
f
[
i
]
≤
f
[
j
]
i \leq j,f[i] \leq f[j]
i≤j,f[i]≤f[j]。
感性理解就是反证,若
f
[
i
]
>
f
[
j
]
f[i] > f[j]
f[i]>f[j],不如把
f
[
i
]
−
>
f
[
j
]
f[i]->f[j]
f[i]−>f[j],这样答案合法且更优。
这样就可以考虑分治。
每次把左端点的区间二分,暴力计算
f
[
m
i
d
]
f[mid]
f[mid],分治下去时就可以
[
l
,
m
i
d
]
[l,mid]
[l,mid] 只考虑右端点
[
1
,
f
[
m
i
d
]
]
[1,f[mid]]
[1,f[mid]],
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r] 同理。
时间复杂度:分治
l
o
g
log
log 层,每层总和为
n
n
n,所以总为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
#include<bits/stdc++.h>
#define N 200005
typedef long long ll;
using namespace std;
inline int read(){
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
return x*f;
}
ll v[N],c[N],b[N],bt,n,m;
struct node{
int v,c;
}pre[N];
bool cmp(node a,node b){return a.c<b.c;}
struct seg{
int ls,rs,cnt;
ll sum;
}t[N<<7];
int tot,root[N];
ll ans[N];
inline int get(int x){return lower_bound(b+1,b+1+bt,x)-b;}
inline int add(){
t[++tot].ls=t[tot].rs=t[tot].sum=t[tot].cnt=0;
return tot;
}
int change(int p,int q,int l,int r,int tl){
t[p]=t[q];
if(l==r){
t[p].cnt++;t[p].sum+=b[tl];
return p;
}
int mid=(l+r)>>1;
if(tl<=mid)t[p].ls=change(add(),t[q].ls,l,mid,tl);
else t[p].rs=change(add(),t[q].rs,mid+1,r,tl);
t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum;
t[p].cnt=t[t[p].ls].cnt+t[t[p].rs].cnt;
return p;
}
ll ask(int q,int p,int l,int r,int k){
if(t[p].cnt-t[q].cnt<k)return -1;
if(k==0)return 0;
if(l==r){
return k*b[l];
}
int mid=(l+r)>>1,rcnt=t[t[p].rs].cnt-t[t[q].rs].cnt;
ll sum=0;
if(k>=rcnt)sum+=ask(t[q].ls,t[p].ls,l,mid,k-rcnt)+t[t[p].rs].sum-t[t[q].rs].sum;
else sum+=ask(t[q].rs,t[p].rs,mid+1,r,k);
return sum;
}
void solve(int l,int r,int pl,int pr){
if(l>r)return ;
if(l==r&&ans[l])return ;
int mid=(l+r)>>1,pos=n;
ans[mid]=-1e15;
for(int i=max(mid,pl);i<=pr;++i){
ll now=ask(root[mid],root[i-1],1,bt,m-2);
if(now==-1)continue;
now=v[mid]+v[i]+now-1ll*2*(c[i]-c[mid]);
if(ans[mid]<now)pos=i;
ans[mid]=max(ans[mid],now);
}
solve(l,mid-1,pl,pos);solve(mid+1,r,pos,pr);
}
int main(){
// freopen("data.in","r",stdin);
n=read(),m=read();
tot=-1;add();
for(int i=1;i<=n;++i)v[i]=read(),c[i]=read(),pre[i].v=v[i],pre[i].c=c[i],b[++bt]=v[i];
sort(pre+1,pre+1+n,cmp);
sort(b+1,b+1+bt);
bt=unique(b+1,b+1+bt)-b-1;
root[0]=0;
for(int i=1;i<=n;++i){
v[i]=pre[i].v;c[i]=pre[i].c;
root[i]=change(add(),root[i-1],1,bt,get(v[i]));
}
solve(1,n,1,n);
ll maxn=-1e15;
for(int i=1;i<=n;++i)maxn=max(maxn,ans[i]);
printf("%lld\n",maxn);
return 0;
}