2151: 种树
Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 1706 Solved: 935
[Submit][Status][Discuss]
Description
A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。
Input
输入的第一行包含两个正整数n、m。第二行n个整数Ai。
Output
输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。
Sample Input
【样例输入1】
7 3
1 2 3 4 5 6 7
【样例输入2】
7 4
1 2 3 4 5 6 7
Sample Output
【样例输出1】
15
【样例输出2】
Error!
恰好选m个,我们可以想到wqs二分。
我们通过减少选的权值,使得我们正好选m个,得到我们的答案。
每次check的时候,我们直接 dp[i][0/1][0/1]表示前i个当中,第1个选没选,第i个选没选的最大价值。
然后如果个数大于等于 m ,那么证明我们减少的权值还不够,那么就增大l即可,否则减少r。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10,inf=0x3f3f3f3f;
int n,m,a[N],res;
struct node{int val,cnt;}dp[N][2][2],t;
node max(node a,node b){
return (a.val>b.val||a.val==b.val&&a.cnt>b.cnt)?a:b;
}
int check(int mid){
memset(dp,0,sizeof dp);
dp[1][1][1]={a[1]-mid,1}; dp[1][1][0]=dp[1][0][1]={-inf,0};
for(int i=2;i<=n;i++){
dp[i][0][0]=max(dp[i-1][0][0],dp[i-1][0][1]);
dp[i][1][0]=max(dp[i-1][1][0],dp[i-1][1][1]);
dp[i][0][1]={dp[i-1][0][0].val+a[i]-mid,dp[i-1][0][0].cnt+1};
dp[i][1][1]={dp[i-1][1][0].val+a[i]-mid,dp[i-1][1][0].cnt+1};
}
t=max(dp[n][0][0],max(dp[n][0][1],dp[n][1][0]));
return t.cnt>=m;
}
int wqs(){
int l=-1e3,r=1e3;
while(l<=r){
int mid=l+r>>1ll;
if(check(mid)) l=mid+1,res=t.val+m*mid;
else r=mid-1;
}
return res;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
if(m*2>n) return puts("Error!"),0;
cout<<wqs()<<endl;
return 0;
}