猜想一下这个函数是分奇偶凸的。
事实上也的确是这样。
考虑分治之后合并的过程。已经处理出了左右两边选多少个的最大值和最小值。
然后按照奇偶分类地合并即可得到新的值。
可以归纳地证明他是凸的。考虑更新的最优解如何求得即可。每次必定是左边多选两个或者右边多选两个,取最优的即可。
令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..b−x]将会小于等于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]);
}