「ケーキの貼り合わせ (Cake 3)」Solution

简述题意

你要从 n n n 个蛋糕中选择 m m m 个,每个蛋糕有 v , c v,c v,c 两个属性,令你选择的蛋糕编号为 k 1 k_1 k1 k m k_m km,请最大化:

∑ i = 1 m v k i − ∑ i = 1 m ∣ c k i − c k i + 1 ∣ \sum_{i=1}^{m} v_{k_i} - \sum_{i=1}^{m} | c_{k_i} - c_{k_{i+1}} | i=1mvkii=1mckicki+1

注意 k m + 1 = k 1 k_{m+1}=k_1 km+1=k1

思路

从贪心的角度思考,为了使 ∑ i = 1 m ∣ c k i − c k i + 1 ∣ \sum_{i=1}^{m} | c_{k_i} - c_{k_{i+1}} | i=1mckicki+1 尽可能地小,那么就一定有 c k i ≤ c k i + 1 c_{k_i} \le c_{k_{i+1}} ckicki+1,证明显然,考虑此时交换 k k k 中的某对元素,那么一定不优。
所以,不妨将蛋糕按 c c c 从小到大排序,那么就等价于求 ∑ i = 1 m v k i − 2 ∗ ( c k m − c 1 ) \sum_{i=1}^{m} v_{k_i} - 2*(c_{k_m}-c_1) i=1mvki2(ckmc1) 的最大值。

不妨令 c o s t ( i , j ) cost(i,j) cost(i,j) 表示 i , j i,j i,j 必选,从 [ i , j ] [i,j] [i,j] 中选出 m m m 个蛋糕的最大答案,那么显然有:

c o s t ( i , j ) = v i + v j − 2 ∗ ( c j − c i ) + m i d v a l cost(i,j)=v_i+v_j-2*(c_j-c_i)+midval cost(i,j)=vi+vj2(cjci)+midval

其中 m i d v a l midval midval 表示 [ i + 1 , j − 1 ] [i+1,j-1] [i+1,j1] 中前 k − 2 k-2 k2 大的 v v v 的总和。特定区间前 k k k 大总和,很容易想到用主席树维护。
那么最终答案为 c o s t ( i , j ) , i , j ∈ [ 1 , n ] cost(i,j),i ,j\in [1,n] cost(i,j),i,j[1,n],然而时间复杂度 O ( n 2 ) O(n^2) O(n2) 显然不优。

考虑决策单调性,其实不需要严格证明,把模板写出来套进去,大样例过了一般就是对的

但还是简单提一下,注意到式子中 c o s t ( i , j ) + 2 × c j cost(i,j)+2 \times c_j cost(i,j)+2×cj 满足四边形不等式,令 d p i dp_i dpi 为钦定 i i i 为结尾的最大值,有:

d p i = c o s t ( i , j ) + 2 × ( c j − c i ) , j ∈ [ 1 , i − k + 1 ] dp_i=cost(i,j)+2\times(c_j-c_i),j \in [1,i-k+1] dpi=cost(i,j)+2×(cjci)j[1,ik+1]

分治处理一下就好了。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e5 + 5;
int n , k , dp[MAXN] , lsh[MAXN] , len;
struct node{
	int v , c;
	friend bool operator<(node x , node y) {return x.c < y.c;}
}a[MAXN];
namespace Segement{
	static int tot , rt[MAXN];
	struct tree{
		int l , r , ls , rs , cnt , sum;
	}tree[MAXN << 5];
	int newnode(int p) {
		tree[++ tot] = tree[p];
		return tot;
	}
	int build(int p , int l , int r) {
		p = ++ tot;
		tree[p].l = l , tree[p].r = r;
		if (l == r) return p;
		int mid = l + r >> 1;
		tree[p].ls = build(p , l , mid) , tree[p].rs = build(p , mid + 1 , r);
		return p;
	}
	int update(int p , int x) {
		int now = newnode(p);
		tree[now].cnt ++ , tree[now].sum += lsh[x];
		if (tree[now].l == tree[now].r) return now;
		int mid = tree[p].l + tree[p].r >> 1;
		if (x <= mid) tree[now].ls = update(tree[now].ls , x);
		else tree[now].rs = update(tree[now].rs , x);
		return now;
	}
	int query(int p , int p2 , int kth) {
		if (tree[p].l == tree[p].r) {
			return kth * lsh[tree[p].l];
		}
		int cnt = tree[tree[p2].rs].cnt - tree[tree[p].rs].cnt;
		if (cnt < kth) return tree[tree[p2].rs].sum - tree[tree[p].rs].sum + query(tree[p].ls , tree[p2].ls , kth - cnt);
		return query(tree[p].rs , tree[p2].rs , kth);
	}
} 
using namespace Segement;
int cost(int l , int r) {
	if (k == 2) return 0;
	return query(rt[l - 1] , rt[r] , k - 2);
}
void DP(int l , int r , int ql , int qr) {
	if (l > r) return;
	int mid = l + r >> 1 , qmid = ql;
	for (int i = ql ; i <= min(qr , mid) ; i ++) {
		if (mid - i + 1 < k) continue;
		int lst = cost(i + 1 , mid - 1) + lsh[a[i].v] + lsh[a[mid].v] - 2 * (a[mid].c - a[i].c);
		if (lst > dp[mid]) dp[mid] = lst , qmid = i;
	}
	DP(l , mid - 1 , ql , qmid);
	DP(mid + 1 , r , qmid , qr);
}
signed main() {
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> k;
	for (int i = 1 ; i <= n ; i ++) cin >> a[i].v >> a[i].c;
	sort(a + 1 , a + n + 1);
	if (k == 1) {
		int Max = -1e18;
		for (int i = 1 ; i <= n ; i ++) Max = max(Max , a[i].v);
		cout << Max;
	} else {
		for (int i = 1 ; i <= n ; i ++) lsh[++ len] = a[i].v;
		sort(lsh + 1 , lsh + len + 1);
		len = unique(lsh + 1 , lsh + len + 1) - lsh - 1;
		rt[0] = build(1 , 1 , len);
		for (int i = 1 ; i <= n ; i ++) {
			a[i].v = lower_bound(lsh + 1 , lsh + len + 1 , a[i].v) - lsh;
			rt[i] = update(rt[i - 1] , a[i].v);
		}
		memset(dp , -0x3f , sizeof(dp));
		DP(1 , n , 1 , n);
		int Max = -1e18;
		for (int i = 1 ; i <= n ; i ++) Max = max(Max , dp[i]);
		cout << Max;
	}
	return 0;
}

posted @ 2024-04-17 00:03  Fracture_Dream  阅读(20)  评论(0)    收藏  举报  来源