6.22模拟 T1 小蛋糕(分治做决策单调性)

题目大意:


在这里插入图片描述

3<=m<=n<=1e5

题解:


显然是C由小到大是最优的,那么这时代价就是(最大-最小)*2
先把把所有蛋糕按c排序。

不然发现随着左端点的递增,最优决策的右端点是非递减的。

然后这个东西居然可以分治???(套路太浅)

d g ( x , y , l , r ) dg(x,y,l,r) dg(x,y,l,r)表示现在要搞左端点在 [ x , y ] [x,y] [x,y]里的,它们可能的最优决策区间是 [ l , r ] [l,r] [l,r]

m = ( x + y ) / 2 m=(x+y)/2 m=(x+y)/2,m的最优决策点是u,这个可以通过暴力扫描 [ l , r ] [l,r] [l,r]得到。

那么 d g ( x , m − 1 , l , u ) , d g ( m + 1 , y , u , r ) dg(x,m-1,l,u),dg(m+1,y,u,r) dg(x,m1,l,u),dg(m+1,y,u,r)

假设query一次的复杂度是 O ( 1 ) O(1) O(1)的,那么这个做法的复杂度是 O ( n   l o g   n ) O(n~log~n) On log n的。

可以这么分析,每一层的query次数最多是 O ( n + 段 数 ) = O ( n ) O(n+段数)=O(n) O(n+)=O(n)的,一共有 O ( l o g   n ) O(log~n) O(log n)

query可以通过预处理一个主席树来线段树二分前V前 m − 2 m-2 m2大的和来做

总复杂度是 O ( n   l o g 2   n ) O(n~log^2~n) O(n log2 n)

Code:


#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 2e5 + 5;

int n, m;
struct P {
	ll x, y;
	P(ll _x = 0, ll _y = 0) { x = _x, y = _y;}
} a[N];
int cmp(P a, P b) { return a.y < b.y;}
P operator + (P a, P b) { return P(a.x + b.x, a.y + b.y);}
P operator - (P a, P b) { return P(a.x - b.x, a.y - b.y);}

int b[N], tb[N];
int cmp2(int x, int y) { return a[x].x < a[y].x;}

struct tree {
	int l, r;
	P a;
} t[N * 30];
#define i0 t[i].l
#define i1 t[i].r
int g[N], pl, pr, tot;
void add(int &i, int x, int y) {
	if(y < pl || x > pr) return;
	t[++ tot] = t[i]; i = tot;
	if(x >= pl && y <= pr) {
		t[i].a = P(1, a[b[x]].x);
		return;
	}
	int m = x + y >> 1;
	add(i0, x, m); add(i1, m + 1, y);
	t[i].a = t[i0].a + t[i1].a;
}
P px;
void ft(int g1, int g2, int x, int y) {
	if(x == y) {
		if(pl - px.x > 0) px = px + t[g2].a - t[g1].a;
		return;
	}
	int m = x + y >> 1;
	int r1 = t[g1].r, r2 = t[g2].r;
	if((t[r2].a - t[r1].a).x <= pl - px.x) {
		px = px + t[r2].a - t[r1].a;
		ft(t[g1].l, t[g2].l, x, m);
	} else ft(r1, r2, m + 1, y);
}

ll query(int x, int y) {
	px = P(0, 0); pl = m - 2;
	ft(g[x], g[y - 1], 1, n);
	return px.y + -2 * (a[y].y - a[x].y) + a[x].x + a[y].x;
}

ll ans = -1e18;
void dg(int x, int y, int l, int r) {
	if(x > y) return;
	if(x == y) {
		fo(i, l, r) if(x + m - 1 <= i)
			ans = max(ans, query(x, i));
		return;
	}
	int mi = x + y >> 1;
	int st = max(l, mi + m - 1);
	if(st > r) {
		dg(x, mi - 1, l, r);
		return;
	}
	int u = st; ll v = query(mi, u);
	fo(i, st + 1, r) {
		ll v2 = query(mi, i);
		if(v2 > v) u = i, v = v2;
	}
	ans = max(ans, v);
	dg(x, mi - 1, l, u);
	dg(mi + 1, y, u, r);
}

int main() {
	freopen("cake3.in", "r", stdin);
	freopen("cake3.out", "w", stdout);
	scanf("%d %d", &n, &m);
	fo(i, 1, n) scanf("%lld %lld", &a[i].x, &a[i].y);
	sort(a + 1, a + n + 1, cmp);
	fo(i, 1, n) b[i] = i;
	sort(b + 1, b + n + 1, cmp2);
	fo(i, 1, n) tb[b[i]] = i;
	fo(i, 1, n) {
		g[i] = g[i - 1];
		pl = pr = tb[i];
		add(g[i], 1, n);
	}
	dg(1, n, 1, n);
	pp("%lld\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值