经典模型:在n个点中选k个点,要求两两不相邻,且总权值最大/最小。

  做法:用双向链表串起来,把所有点丢进堆里,选择一个点的时候把它左右两个点从双向链表和堆中去除,然后把这个点的权值加进ans,出堆后改为左右两边的权值-当前权值重新入堆,重复k次,ans即为答案

  原理:左右两边的权值-当前权值相当于这个点不选,改为选左右两边的点,并且多选了一个点,重复k次后必然取到k个点

  三道同类型题

  bzoj1150:显然一段网线肯定接在相邻的两个点,于是把相邻的点的距离求出来,问题就变成经典模型了

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
#define ll long long 
using namespace std;
const int maxn=500010,inf=1e9;
struct poi{int pos,dis;};
priority_queue<poi>q;
bool operator<(poi a,poi b){return a.dis>b.dis;}
int n,k,a[maxn],dis[maxn],pre[maxn],next[maxn];
ll ans;
bool v[maxn];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
    while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
    k*=f;
}
inline void del(int x)
{
    int l=pre[x],r=next[x];
    pre[x]=next[x]=0;
    next[l]=r;pre[r]=l;
}
int main()
{
    read(n);read(k);
    for(int i=1;i<=n;i++)read(a[i]);
    for(int i=1;i<n;i++)dis[i]=a[i+1]-a[i],q.push((poi){i,dis[i]});dis[0]=dis[n]=inf;
    for(int i=1;i<n;i++)pre[i]=i-1,next[i]=i+1;
    for(int i=1;i<=k;i++)
    {
        poi t=q.top();
        while(v[t.pos])q.pop(),t=q.top();
        ans+=t.dis;v[pre[t.pos]]=v[next[t.pos]]=1;
        dis[t.pos]=dis[pre[t.pos]]+dis[next[t.pos]]-dis[t.pos];
        q.pop();q.push((poi){t.pos,dis[t.pos]});
        del(pre[t.pos]);del(next[t.pos]);
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

相关文章: