[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 }
View Code

 

posted @ 2021-02-02 13:24  PYWBKTDA  阅读(361)  评论(0编辑  收藏  举报