[gym102822I]Invaluable Assets
令$f(x)=\frac{x^{2}+c}{x}$,换言之即$x$物品的性价比的倒数
对其求导即$f'(x)=1-\frac{c}{x^{2}}$,其导数严格递增,换言之即是一个严格下凸函数,记$x_{0}$为其最小值的位置,那么不难证明$x_{0}=\lfloor\sqrt{c}\rfloor$或$\lceil\sqrt{c}\rceil$
性质1:存在最优解,使得其不存在一个$x\ge 2x_{0}$的物品
对于$x\ge 2x_{0}$的物品,考虑拆分为$x_{0}+(x-x_{0})$,代入化简即求证$2x_{0}(x-x_{0})\ge c$,由于$x\ge 2x_{0}$,即要求$2x_{0}^{2}\ge c$,显然成立
性质2:存在最优解,使得不存在若干个体积非$x_{0}$的物品,且体积和为$x_{0}$的倍数
将这些物品用若干个$x_{0}$来代替是不劣的——
从性价比的角度来考虑,对于两个物品合并后的性价比显然在合并前两物品的性价比区间中
由于$x_{0}$的性价比最大(性价比倒数最小),因此其余物品合并后性价比一定不超过$x_{0}$,而仅含有$x_{0}$的话一定恰好为$x_{0}$的性价比,即不劣
推论1:存在最优解,使得不存在$x_{0}$个体积非$x_{0}$的物品
对于$x_{0}$个物品,必然会存在一个子集使得其体积和为$x_{0}$的倍数
推论2:存在最优解,使得体积非$x_{0}$的物品和不超过$3c$
根据推论1,至多$x_{0}-1$个物品,根据性质1,每一个物品体积不超过$2x_{0}-1$,显然$(x_{0}-1)(2x_{0}-1)\le 3c$,即成立
设$V$是需要填充的体积($\max(k-a_{i},0)$),对$V\le 3c$用背包预处理,复杂度为$o(c\sqrt{c})$(物品体积小于$2x_{0}$),接下来考虑$V>3c$的问题:
性质3:存在一组最优解,使得其至多选择两种体积的物品,且为$x$和$x+1$
当我们同时选择了$x$和$y$两个物品,且满足$x+1\le y-1$,根据凸性(或代入展开),不难得到选择$x+1$和$y-1$一定不劣,即可以使得其不存在
推论3:当$V>3c$,$x=x_{0}$或$x_{0}-1$
若$x\ne x_{0}$或$x_{0}-1$,即$x_{0}\notin \{x,x+1\}$,那么即不为$x_{0}$的物品体积和大于了$3c$,矛盾
以$x=x_{0}$为例,先选择$s$个$x_{0}$,再将其中$V-sx_{0}$个个物品变为$x_{0}+1$,由于$x_{0}+1$的物品个数要小于$x_{0}$(根据推论1),因此是$s$是唯一的
通过上述,我们填充$V$体积的最优解可以$o(1)$得到,复杂度为$o(c\sqrt{c}+nq)$,无法通过
接下来,由于$k$和$a_{i}$是随机的,因此$V\le 3c$的概率为$o(\frac{c}{1e9})$,对于这一类暴力统计,复杂度为$o(c\sqrt{c}+\frac{nqc}{1e9})$(具体可以将$k$排序后利用单调性来找)
对于$V>3c$的情况,继续分析不难发现答案即
$$
(x_{0}^{2}+c)\frac{V-r}{x_{0}}+\min((2x_{0}+1)r,(x_{0}^{2}+c)+(x_{0}-r)(2x_{0}-1))
$$
(其中$V\equiv r(mod\ x_{0})$且$0\le r<x_{0}$)
后者仅与$r$有关,利用单调性,维护出$a_{i}$模$x_{0}$的每一类中的个数以及$V$的和,即可做到$o(q\sqrt{c})$的复杂度
最终总复杂度为$o(c\sqrt{c}+\frac{nqc}{1e9}+q\sqrt{c})$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define C 10005 5 #define X0 105 6 #define ll long long 7 #define fi first 8 #define se second 9 pair<int,int>q[N]; 10 int t,n,c,m,x0,a[N],f[C*3],sum1[X0]; 11 ll sum2[X0],ans[N]; 12 int cost(int k){ 13 return 1LL*k*k+c; 14 } 15 int calc(int k){ 16 return min((2*x0+1)*k,cost(x0)-(x0-k)*(2*x0-1)); 17 } 18 int main(){ 19 scanf("%d",&t); 20 for(int ii=1;ii<=t;ii++){ 21 scanf("%d%d%d",&n,&c,&m); 22 x0=(int)sqrt(c); 23 if (cost(x0)*(x0+1)>cost(x0+1)*x0)x0++; 24 memset(f,0x3f,sizeof(f)); 25 f[0]=0; 26 for(int i=1;i<2*x0;i++) 27 for(int j=i;j<=3*c;j++)f[j]=min(f[j],f[j-i]+cost(i)); 28 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 29 sort(a+1,a+n+1); 30 for(int i=1;i<=m;i++){ 31 scanf("%d",&q[i].fi); 32 q[i].se=i; 33 } 34 sort(q+1,q+m+1); 35 memset(sum1,0,sizeof(sum1)); 36 memset(sum2,0,sizeof(sum2)); 37 memset(ans,0,sizeof(ans)); 38 for(int i=1,l=1,r=1;i<=m;i++){ 39 while ((l<=n)&&(a[l]<q[i].fi-3*c)){ 40 sum1[a[l]%x0]++; 41 sum2[a[l]%x0]+=a[l]; 42 l++; 43 } 44 while ((r<=n)&&(a[r]<=q[i].fi))r++; 45 for(int j=l;j<r;j++)ans[q[i].se]+=f[q[i].fi-a[j]]; 46 for(int j=0;j<x0;j++){ 47 int jj=(q[i].fi-j)%x0; 48 ans[q[i].se]+=1LL*sum1[j]*calc(jj)+(1LL*sum1[j]*(q[i].fi-jj)-sum2[j])/x0*cost(x0); 49 } 50 } 51 printf("Case #%d:\n",ii); 52 for(int i=1;i<=m;i++)printf("%lld\n",ans[i]); 53 } 54 }