凸函数相关问题
基础知识
- 一个函数f是上凸函数,当且仅当对于任意a<c,记录b=(a+c)/2,有2f(b)≥f(a)+f(c)。
- 一个函数f是下凸函数,当且仅当对于任意a<c,记录b=(a+c)/2,有2f(b)≤f(a)+f(c)。
- 如果函数f在定义域上二次可微,且f″≥0,则f是下凸函数,若f″≤0,则f是上凸函数。
- 函数ax是下凸函数,如果a≥0。
- 函数lnx是上凸函数,exp(x)是下凸函数。
- 函数xn在[0,+∞)上,若0≤n≤1,则为上凸函数,若x≥1,则为下凸函数。
命题1:如果函数f和函数g都是上凸(下凸)函数,则它们的和f+g也是上凸(下凸)函数。
证明:略。
命题2:如果函数f和函数g都是上凸函数,则它们的和min(f,g)也是上凸函数。
证明:略。
命题3:如果函数f和函数g都是下凸函数,则它们的和max(f,g)也是下凸函数。
证明:略。
命题4:如果函数f,g是上凸(下凸)函数,且f递增,则f(g)也是上凸(下凸)函数。
证明:略。
最大和问题
题目1:给定m个线性函数f1,…,fm,其中fi(x)=aix+bi,现在我们希望为m个非负整数未知变量x1,…,xm赋值,且要求满足maxmi=1xi=n,在满足约束条件下,要求∑ni=1fi(xi)尽可能大。
我们可以这样来理解这个问题,首先我们有m个可投资对象和n个资源,且已经获利∑mi=1bi,接下来如果我们为第i个可投资对象每投入一单位资源,那么就能获利ai,问最大获利多少。
这个自然是将所有资源都砸在ai最大的可投资对象上了。
题目2:给定m个函数f1,…,fm,且每个函数的成像都构成一个上凸包,现在我们要求为m个非负整数未知变量x1,…,xm赋值,且要求满足∑mi=1xi=n,在满足约束条件下,要求∑ni=1fi(xi)尽可能大。其中1≤m,n≤105
利用题目1的方式,我们来试图重新解读问题。首先我们有m个可投资对象和n个资源,且已经获利∑mi=1fi(0),接下来如果我们为第i个可投资对象每投入一单位资源,那么就能获利fi(x+1)−fi(x)(x是已经向第i个可投资对象投入的资源总量),问最大获利多少。
我们会发现由于所有的函数都是上凸包,因此对于任意i,di(x+1)=fi(x+1)−fi(x)是非严格递减函数。考虑到di(1)≥di(2)≥…,因此在最终结果中,如果选择di(x+1)那么我们必定也会选择di(x),因此我们可以将所有可选的收益一开始就全部展开di(x),现在我们要做的就是从di(x)中选择正好n个元素,且元素之和最大。这个步骤自然我们会选择使用贪心算法来完成。
现在回到问题,如果我们把所有方案都展开,那么可能会有nm个选择,这样太多了,但是由于我们在取得di(x)之前是不会考虑di(x+1)的,因此我们可以对每个i,仅考虑x最小且未被选择的di(x)。实际操作中,我们只需要维护一个大小为m的大根堆即可,当我们将di(x)弹出的同时,将di(x+1)加回即可,总的时间复杂度为O(nlog2m)。
提供一道SGU的问题。
题目3:给定m个函数f1,…,fm,且每个函数的成像都构成一个上凸包,现在我们要求为m个非负整数未知变量x1,…,xm赋值,且要求满足∑mi=1xi=n,在满足约束条件下,要求∑ni=1fi(xi)尽可能大。其中1≤m≤105,1≤n≤1018
这个问题是今天比赛的一道题目学到的。由于是上凸包,因此我们可以用三分算法为每个函数找到其极值点(在非负整数定义域下),记fi的极值点在pi处取到。
分两种情况讨论,如果n≤∑mi=1pi,那么我们可以断定一定有xi≤pi,否则对应的就有xi≥pi。
如果是n≤∑mi=1pi的情况。记di(x)=fi(x)−fi(x−1)称为函数fi在点x处的增量。很显然最优解中,采用的最小增量一定是最大的,我们可以通过二分法找到这个最小增量,这里的时间复杂度为O(n(log2M)2),其中M是数据范围。找到最小增量δ后,我们将xi设置为fi增量大于等于δ的整数点数目(不包括0)。那么此时可能会有∑mi=1xi>n的情况出现,我们可以保证所有函数增量大于δ的整数点都会被保留(否则为什么要使用增量为δ的点呢?),而增量为δ的点可以删除一部分,来满足∑mi=1xi>n,这一部分的时间复杂度为O(m)。
对于n≥∑mi=1pi的情况,与上面的方法类似。
最后总的时间复杂度为O(m(log2M)2)。
提供一道题目。
题目4:给定n个非严格递增数组a1,a2,…,an,长度分别为t1,…,tn。之后要求执行正好m次操作,每次操作选择一个非空的数组头,并且删除数组头部元素。要求m次操作后删除的元素总和最大。输入满足1≤n,m≤3000,且m≤∑ni=1ti≤106,每个数组元素在0到109之间。
一般的DP做法时间复杂度为O(nm∑ni=1ti),太慢了。
很显然我们最多只会有一个数组的元素是部分取走的。因此就得出了我们的思路,一种预处理前后缀的做法的时间复杂度为O(nm2+∑ni=1ti),还是不够好。
这里我们可以神奇的套上分治。类似与并行二分,我们可以将要处理的数组分为两部分,之后在左边的数组上暴力计算DP,并在递归处理右部分的时候作为初始值下传。可以发现抵达叶子(区间只有一个元素)的时候,这时候其余元素都被处理到了。
树的每一层的时间复杂度都是O(nm),最后一层的时间复杂度为O(∑ni=1ti)。因此总的时间复杂度为O(nmlog2n+∑ni=1ti)。
提供一道题目。
并且由于背包问题中交换商品顺序不影响结果,因此可以用可撤销队列来做,时间复杂度也是O(nmlog2n+∑ni=1ti)。
题目5:给定m个函数f1,…,fm,且每个函数的成像都构成一个上凸包,且每个函数的值域都是正数,现在我们要求为m个非负整数未知变量x1,…,xm赋值,且要求满足∑mi=1xi=n,在满足约束条件下,要求∏ni=1fi(xi)尽可能大。其中1≤m≤105,1≤n≤1018。输出最大可能的乘积,结果对P取模。
我们希望∏ni=1fi(xi)最大,等价于希望∑ni=1lnfi(xi)最大。考虑到lnfi也是个上凸包,因此我们可以用问题3的方式找到每个函数的赋值方案,之后算出真实的乘积即可。时间复杂度为O(nlog2mlog2M)。