Loading

ABC218H Red and Blue Lamps

ABC218H Red and Blue Lamps

题意

\(N\)个位置染色,如果\(A_i\)\(A_{i+1}\)不同色,则获得\(a_i\) ,要求涂\(r\)个红色,\(n-r\)个蓝色

\[N \leq 2e5 \]

分析

显然会选择贪心地涂间隔\(r\)个颜色\(r \leq n /2\)

问题转化为从\(n\)个物品中选取\(r\)个不相邻物品使得总价值最大

直接朴素的令\(dp[i][j]\)表示前\(i\)个物品选择\(j\)个的做法复杂度很难下降

WQS二分可以把这样的问题转化为任意选择物品个数下的最大价值

\(g(x)\)表示选择\(x\)个物品的最大价值,显然这是一个凸函数 ,凸函数的性质就是切线斜率具有单调性。

1.二分斜率

对这个凸包的斜率进行二分,如果能通过二分得到点\(x\),就可以比较\(x\)和已知的限制\(r\)来偏移二分的区间

2.计算截距

对于一个固定的斜率,想要寻找和它切的点,只需要找到和\(y\)轴截距最大的点(画个图可以明白)

亦即\(f(x)= g(x) - kx\) 只需要求出\(f_{max}\)即可,考察这个函数的意义:就是每选择一个物品,总价值减\(k\) 下的最大价值,这个东西就是个简单DP

这样就能记录取到\(f_{max}\)时的\(x\)

3. 把二分得到的值再把斜率加回去

然后就做完了,注意实数

代码

#include<bits/stdc++.h>
#define pii pair<long long,long long>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> VI;


inline ll rd(){
    ll x;
    scanf("%lld",&x);
    return x;
}

/*
inline int rd(){
	int x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x;
}*/

const int MOD = 23333333;

inline int mul(int a,int b){
    int res = (ll)a * b % MOD;
    if(res < 0) res += MOD;
    return res;
}

inline void add(int &a,int b){
    a += b;
    if(a >= MOD) a -= MOD;
}

inline void sub(int &a,int b){
    a -= b;
    if(a < 0) a += MOD;
}


ll gcd(ll a,ll b){
	return !b ? a : gcd(b,a % b);
}

const int maxn = 2e5 + 5;

int n,r;
int a[maxn];

pair<long double,int> dp[maxn][2];

pair<long double,int> check(long double v){
	dp[n + 1][0] = dp[n + 1][1] = make_pair(0,0);
	for(int i = n;i >= 1;i--){
		for(int j = 0;j < 2;j++){
			auto r0 = dp[i + 1][0],r1 = dp[i + 1][1];
			if(!j) r1.fi += a[i];
			else r0.fi += a[i];
			dp[i][j] = max(r0,r1);
			if(!j) 
				dp[i][j].se++,dp[i][j].fi -= v;
		}
	}
	return max(dp[1][0],dp[1][1]);
}


int main(){
	n = rd();
	r = rd();
	for(int i = 1;i <= n - 1;i++)
		a[i] = rd();
	long double L = -1e15,R = 1e15;
	for(int i = 0;i < 100;i++){
		long double mid = (L + R) / 2.0;
		auto it = check(mid);
		if(it.se < r) R = mid;
		else L = mid;
	}
	auto it = check(L);
	ll ans = (ll)(it.fi + r * L);
	printf("%lld",ans);	
}
posted @ 2021-09-12 21:11  MQFLLY  阅读(89)  评论(0编辑  收藏  举报