jzoj6355 【NOIP2019模拟】普(pe) (闵可夫斯基和)

jzoj6355 【NOIP2019模拟】普(pe) (闵可夫斯基和)_第1张图片
jzoj6355 【NOIP2019模拟】普(pe) (闵可夫斯基和)_第2张图片

分析

猜想一下这个函数是分奇偶凸的。
事实上也的确是这样。

考虑分治之后合并的过程。已经处理出了左右两边选多少个的最大值和最小值。
然后按照奇偶分类地合并即可得到新的值。

可以归纳地证明他是凸的。考虑更新的最优解如何求得即可。每次必定是左边多选两个或者右边多选两个,取最优的即可。
令f,g是这两个凸函数的差分,则f,g是递减函数。H是要更新的凸函数。
一个直观的理解是,若 f [ 1.. a ] + g [ 1.. b ] 是 H [ a + b ] f[1..a]+g[1..b]是H[a+b] f[1..a]+g[1..b]H[a+b]的一组最优的更新,那么 f [ 1.. a + x ] + g [ 1.. b − x ] f[1..a+x]+g[1..b-x] f[1..a+x]+g[1..bx]将会小于等于h[a+b]。若x!=0,作差可发现,再在这种情况上多取一个f是不如x=0的情况优的,因为f是递减的。

这个东西可以推广到求闵可夫斯基和的凸包上,具体证明大致是差不多的。
也即,求两个凸壳的闵可夫斯基和的凸壳,只需要用两个指针扫,每次取斜率更合适的那一边即可。

#include 
using namespace std;
const int N = 1e5 + 10, inf = 2e9 + 100;
int a[N], n;

vector<int> mx[N], mi[N];
int sz,lsz,rsz;

void merge(vector<int> &r, vector<int> &a, vector<int> &b, int na, int nb, int sr, int sig,int w) {
	for(int i = sr; i <= sz; i += 2) {
		if (na > lsz || nb > rsz) break;
		if (sig == 0) r[i] = max(r[i], a[na] + w * b[nb]);
		else r[i] = min(r[i], a[na] + w * b[nb]);
		if (na + 2 > lsz) nb += 2; else
		if (nb + 2 > rsz) na += 2; else
		if (sig ^ (a[na + 2] + w * b[nb] > a[na] + w * b[nb + 2])) {
			na += 2;
		} else nb += 2;
	}
}

void write(vector<int> x) {
	for(int a : x) {
		printf("%d ", a);
	}
	printf("\n");
}

void divide(int l,int r) {
	if (l == r) {
		mx[l].resize(2);
		mi[l].resize(2);
		mx[l][0]=mi[l][0]=0;
		mx[l][1]=mi[l][1]=a[l];
		return;
	}
	
	int mid = (l + r) >> 1;
	
	divide(l, mid);
	divide(mid + 1, r);
	sz = r - l + 1;
	lsz = mid - l + 1, rsz = r - mid;
	
	vector<int> tmx, tmi;
	tmx.clear(), tmi.clear();
	tmx.resize(sz + 1, -inf);
	tmi.resize(sz + 1, inf);
	
	mid++;
	merge(tmx, mx[l], mx[mid], 0, 0, 0, 0, 1);
	merge(tmx, mx[l], mi[mid], 1, 1, 2, 0, -1);
	merge(tmx, mx[l], mx[mid], 0, 1, 1, 0, 1);
	merge(tmx, mx[l], mi[mid], 1, 0, 1, 0, -1);
	
	merge(tmi, mi[l], mi[mid], 0, 0, 0, 1, 1);
	merge(tmi, mi[l], mx[mid], 1, 1, 2, 1, -1);
	merge(tmi, mi[l], mi[mid], 0, 1, 1, 1, 1);
	merge(tmi, mi[l], mx[mid], 1, 0, 1, 1, -1);
	
	mx[l].resize(sz + 1); mi[l].resize(sz + 1);
	copy(tmx.begin(), tmx.end(), mx[l].begin());
	copy(tmi.begin(), tmi.end(), mi[l].begin());
	// printf("%d %d\n",l,r);write(tmx);write(tmi);
	// printf("\n");
}

int main() {
	freopen("pe.in","r",stdin);
	freopen("pe.out","w",stdout);
	
	cin>>n;
	for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
	divide(1, n);
	for(int i = 1; i <= n; i++) printf("%d ", mx[1][i]);
}

你可能感兴趣的:(题解,新内容)