(\)
(Description)
给出一个数轴上(N)个点的坐标(A_i),选择(K)个点对,使得这(K)个点对每个点对的距离之和尽可能小。
- (Nin [0,10^5]),(Kin [0,frac{N}{2}]),(A_iin [0,10^9])
(\)
(Solution)
- 排序,求出相邻两两间距(D_i),问题转化为选(K)个互不相邻的间距。
- 将所有的间距放到一个小根堆中,每次取堆顶加入答案。如果选择了(D_i),则在堆中删去(D_i,D_{i-1},D_{i+1}),并将(D_{i-1}+D_{i+1}-D_i)加入堆中,去执行选(K-1)个值得子问题。
- 这样做法的合理性在于,如果每次选则的是原来的(D_i),那么就是直接加入这个答案,如果选择的是某次操作后的(D_{i-1}+D_{i+1}-D_i),那么代表(D_i)在此前一定加入过答案,此时答案里累加上这个数代表,从答案中去掉原来选择的(D_i),加入(D_i)两侧的数,同样会使得选则的间距(+1)。
- 注意,在合并边界元素时,如果再次进行反转操作并不会使选中的段数增加,所以如果边界被合并了一次重置该位置数字时应设为正无穷。
- 可以发现几次操作以后前驱后继就很难寻找了,所以可以采用链表来维护这个数列,同时在插入堆中时绑定链表节点的编号,此时操作即改为合并三个链表元素,标记堆中三个元素不合法,执行(K)次即可选出合法的(K)段间距,即(K)个点对。
(\)
(Code)
#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define R register
#define gc getchar
#define N 100010
#define inf 1000000000
using namespace std;
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
bool vis[N];
int n,m,ans,pre[N],nxt[N],num[N];
priority_queue<pair<int,int> > q;
int main(){
n=rd(); m=rd();
for(R int i=2,lp=rd(),now;i<=n;++i){
pre[i]=i-1; nxt[i]=i+1;
num[i]=(now=rd())-lp; lp=now;
}
pre[2]=nxt[n]=0;
for(R int i=2;i<=n;++i) q.push(make_pair(-num[i],i));
while(m--){
while(vis[q.top().second]) q.pop();
int now=q.top().second;
int pr=pre[now],nx=nxt[now];
q.pop(); ans+=num[now];
pre[nxt[now]=nxt[nx]]=now;
nxt[pre[now]=pre[pr]]=now;
num[now]=(pr&&nx)?min(inf,num[pr]+num[nx]-num[now]):inf;
vis[pr]=vis[nx]=1; q.push(make_pair(-num[now],now));
}
printf("%d
",ans);
return 0;
}
(\)
种树就是把链表循环起来选最大,循环的关系不需要考虑边界的特判比上面的还水
(\)
#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define R register
#define gc getchar
#define N 200010
#define inf 1000000000ll
using namespace std;
typedef long long ll;
inline ll rd(){
ll x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
bool vis[N];
ll n,m,ans,pre[N],nxt[N],num[N];
priority_queue<pair<int,int> > q;
int main(){
n=rd(); m=rd();
if(n<m*2){puts("Error!");return 0;}
for(R ll i=1;i<=n;++i){
pre[i]=i-1; nxt[i]=i+1;
num[i]=rd();
}
pre[1]=n; nxt[n]=1;
for(R ll i=1;i<=n;++i) q.push(make_pair(num[i],i));
while(m--){
while(vis[q.top().second]) q.pop();
ll now=q.top().second;
ll pr=pre[now],nx=nxt[now];
q.pop(); ans+=num[now];
pre[nxt[now]=nxt[nx]]=now;
nxt[pre[now]=pre[pr]]=now;
num[now]=min(inf,num[pr]+num[nx]-num[now]);
vis[pr]=vis[nx]=1; q.push(make_pair(num[now],now));
}
printf("%lld
",ans);
return 0;
}