P3620 [APIO/CTSC 2007] 資料備份 題解
阿新 • • 發佈:2021-08-25
題目大意
數軸上有\(N\)個點,可以用\(K\)條線連線\(2K\)條邊,使得連的邊的總長度最小
問題求解
顯然,連線相鄰的兩個是最好的,所以可以建立\(N-1\)個差分,相鄰的差分不能選,題目也就被轉化成了,\(N-1\)個數裡面選\(K\)個數,相鄰的數不能選,使得數的總和最大
我們很容易可以想到一個貪心,用一個堆每次取小的,講左邊和右邊標記為不可選點,但是也很顯然是錯誤的:\(2 1 2 6\)就可以\(hack\)掉
所以考慮怎麼返回,我們在選一個點時,要在堆中加入一個數,數的大小為左邊數的大小\(+\)右邊數的大小\(-\)自身的大小,連續取這個數兩次就相當於這個數邊上的兩個數了,貪心求解即可
程式碼實現
#include<cstdio> #include<queue> #include<cstring> using namespace std; const int maxn=100005,INF=0x3f3f3f3f; int N,K,Ans,T; int vis[maxn]; struct place{ int val,l,r; }p[maxn]; struct AS{ int val,id; bool operator <(const AS B)const {return val>B.val;} }; priority_queue<AS> q; inline int read(){ int ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar(); return ret*f; } void del(int x){ p[x].l=p[p[x].l].l;p[x].r=p[p[x].r].r; p[p[x].l].r=x;p[p[x].r].l=x; } int main(){ freopen("P3620.in","r",stdin); freopen("P3620.out","w",stdout); T=read(); while(T--){ memset(p,0,sizeof p); while(!q.empty())q.pop(); N=read();K=read();int lst=read(); for(int i=1;i<N;i++){ int now=read(); p[i].val=now-lst; lst=now;p[i].l=i-1;p[i].r=i+1; q.push((AS){p[i].val,i}); } p[0].val=p[N].val=INF; for(int i=1;i<=K;i++){ while(vis[q.top().id]) q.pop(); AS now=q.top();q.pop(); Ans+=now.val;vis[p[now.id].l]=vis[p[now.id].r]=1; p[now.id].val=p[p[now.id].l].val+p[p[now.id].r].val-p[now.id].val; q.push((AS){p[now.id].val,now.id}); del(now.id); } printf("%d\n",Ans); } return 0; }