题意
又到了诺德县的百姓孝敬夹克大老爷的日子,带着数量不等的铜板的村民准时聚集到了村口。
夹克老爷是一位很”善良”的老爷,为了体现他的仁慈,有一套特别的收钱的技巧。
1、让所有的村民排成一队,然后首尾相接排成一个圈。
2、选择一位村民收下他的铜钱,然后放过他左右两边的村民。
3、让上述三位村民离开队伍,并让左右两边的其他村民合拢起来继续围成一个圈。
4、重复执行2、3直到村民全部离开。
夹克老爷的家丁早早的组织村民排成一队并清点了村民人数和他们手里的铜钱数量。
作为夹克老爷的首席师爷,你要负责按照夹克老爷的收钱技巧完成纳贡的任务。
聪明的你当然知道夹克老爷并不像他表现出来的那样仁慈,能否收到最多的钱财决定了你是否能够继续坐稳首席师爷的位置。
今年村民的人数是N,恰巧是3的倍数。
题解
这题自己想出来了啊,可能是以前做过BZOJ2151的缘故吧
在这里说一下怎么做
首先,我们要先转化一下模型
问题等价于n长的数组中抽取
n/3
n
/
3
个不相邻的值使得和最大
我来证明一下:
如果有相邻的元素,肯定是不合法的方案,因为你选择一个,肯定会删去删去第二个
那么就只需要证明如果不相邻就一定合法就可以了
我们考虑一下,如果我们对于一个点,如果删去他不会产生相邻的元素,那么我们就先把他删掉
为什么这么删一定会把元素删完呢?
因为在若干次后,一定会变成只有一段的,每两个元素之间只有一个空格的东西
因为是一个环,所以可以等价于下面这个
红色代表选的元素
然后明显的,最后会有n/2个空格
然后每一次我们删掉最后一个红色就可以了
因为选择的是
n/3
n
/
3
个,后面的空格也是
n/3
n
/
3
个
所以肯定是不会冲突的
于是我们就可以知道通过这种方式删是合法的了
然后考虑解决上面这个问题,就等价于BZOJ2151
这个的过程就是一个可以后悔的贪心啊
建立一个大根堆,每一次给予反悔机会就可以了
CODE:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const LL N=200005;
LL n,k;
LL a[N];
LL last[N],next[N];
struct qq
{
LL id,x;
bool operator < (qq a) const {return x<a.x;}
};
priority_queue <qq> q;
bool lalal[N];//这个点还能不能用
void del (LL x)//将这个点删去
{
last[next[x]]=last[x];
next[last[x]]=next[x];
lalal[x]=false;
}
int main()
{
memset(lalal,true,sizeof(lalal));
scanf("%lld",&n);
for (LL u=1;u<=n;u++)
{
scanf("%lld",&a[u]);
last[u]=u-1;next[u]=u+1;
q.push(qq{u,a[u]});
}
last[1]=n;next[n]=1;
long long ans=0;
for (LL u=1;u<=n/3;u++)
{
qq b;
while (true)
{
b=q.top();
q.pop();
if (lalal[b.id]==true) break;
}
ans=ans+b.x;
LL A=last[b.id],B=next[b.id];
a[b.id]=a[A]+a[B]-b.x;
del(A);del(B);
q.push(qq{b.id,a[b.id]});
}
printf("%lld\n",ans);
return 0;
}