【NOIp模拟赛】种花

【问题描述】

经过三十多个小时的长途跋涉,小Z和小D终于到了NOI现场——南山南中学。一进校园,小D就被花所吸引了(不要问我为什么),遍和一旁的种花园丁交(J)流(L)了起来。

他发现花的摆放竟有如此奥秘:圆形广场共有 N个种花的位置,顺时针编号1到N。并且每个位置都有一个美观度,如果在这里种花就可以得到这的美观度。但由于地处南山土壤肥力欠佳,两株花不能种在相邻的位置(1号和N号也算相邻位置)。校方一共给了 M株花,经过园丁的精妙摆放,才能如此吸引小D。所以现在小D也想知道应该如何摆这 N株花。

【输入格式】

从文件 flower.in 中读入数据。

输入第一行包含两个整数N,M

接下来一行包含N个正整数,依次描述美观度。

【输出格式】

输出到文件 flower.out 中。

输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。

【样例输入】

7 3

1 2 3 4 5 6 7

【样例输出】

15

【数据规模与约定】

对于50%的数据满足。

对于100%的数据满足,。

分析

贪心+堆

我们先把所有点放入堆中,每次取出美观度最大的。如果它不是最优解,那么它旁边的两个肯定是。

这个可以用反证法来证明,如果它旁边的两个不是最优解,那么肯定可以选它,因为他是最大的。

如果他旁边有一个是最优解一个不是最优解,这种情况肯定是不存在的,设它左边的不是最优解,左边肯定不影响,它现在是美观度最大的,选右边的还不如选它。

每次取出最大之后讲
V=Vpre+VnextV加入入堆,最后删掉PreNext,每次用堆维护,取出最大值即可。

上面的公式是很巧妙的,也就是说如果它不是最优解还可以再选一次,只不过这次会把之前的v给减掉,加上它两边的和,而相比之前的那一次正好选了两次。

代码

手打的堆有点长不过速度比STL快。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200000+5;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int n,m,heap_size,ans;
int a[maxn],nxt[maxn],pre[maxn],heap[maxn];
bool vis[maxn];
inline void put(int x)
{
    int now,nxt;
    heap[++heap_size]=x;
    now=heap_size;
    while(now>1)
    {
        nxt=now>>1;
        if(a[heap[now]]<=a[heap[nxt]]) break;
        int t=heap[now]; heap[now]=heap[nxt]; heap[nxt]=t;
        now=nxt;
    }
}
inline int get()
{
    int now=1,nxt,res=heap[1];
    heap[1]=heap[heap_size--];
    while((now<<1)<=heap_size)
    {
        nxt=now<<1;
        if(nxt<heap_size&&a[heap[nxt+1]]>a[heap[nxt]]) nxt++;
        if(a[heap[now]]>=a[heap[nxt]]) break;
        int t=heap[now];heap[now]=heap[nxt]; heap[nxt]=t;
        now=nxt;
    }
    return res;
}
int main()
{
    freopen("flower.in","r",stdin);
    freopen("flower.out","w",stdout);
    n=read(); m=read();
    if((m<<1)>n) { printf("Error!"); return 0;}
    for(int i=1;i<=n;i++) 
    { a[i]=read(); put(i);}
    for(int i=1;i<n;i++) nxt[i]=i+1;
    for(int i=2;i<=n;i++) pre[i]=i-1;
    nxt[n]=1; pre[1]=n;
    for(int i=1;i<=m;i++)
    {
        int x=get();
        while(vis[x])
            x=get();
        ans+=a[x];
        a[x]=a[pre[x]]+a[nxt[x]]-a[x];
        vis[pre[x]]=1; vis[nxt[x]]=1;
        put(x);
        nxt[x]=nxt[nxt[x]];
        pre[x]=pre[pre[x]];
        pre[nxt[x]]=x;
        nxt[pre[x]]=x;
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2017-08-28 20:53  沐灵_hh  阅读(316)  评论(0编辑  收藏  举报