1 条题解
-
0
自动搬运
来自洛谷,原作者为

gxy001
......搬运于
2025-08-24 21:56:36,当前版本为作者最后更新于2022-04-02 11:48:45,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
截止本文提交(2022.4.2)前,洛谷题解区的三篇博客证明均为错误的,希望管理员看到后撤掉。
因为期望的线性性,答案即为 。
题意可以理解为,给 个 间的实数,从中选出 个排成一个序列 ,最小化 ,假设 。
引理 1:选出的 个数按照单调不增排列。
这里需要注意的是,网传的对于降序数组交换两个数后证明答案变大的证法是绝对错误的,这只能证明降序数组要优于降序数组交换两个数的答案,不能证明降序数组比其他所有情况更优,
属于是学 exchange argument 没学会还乱用了。假设我们已经选定 个数为 ,要为这些数定序,此时 是定值,题目要求最小化 ,我们可以转为最大化 $\sum\limits_{i=1}^ka_i-\sum\limits_{i=2}^k(a_i-a_{i-1}a_{i})=1\times a_1+\sum\limits_{i=2}^ka_{i-1}a_{i}$。
假设 降序排序后的数组为 。
由排序不等式得 $1\times a_1+\sum\limits_{i=2}^ka_{i-1}a_{i}\le 1\times b_1+\sum\limits_{i=2}^kb_{i-1}b_{i}$,所以选出的 个数一定按照单调不增排列。
引理 2:选出的 个数是所有数降序排序后的一段前缀加一段后缀。
我们设选择的数下标按顺序为 ,所有 个数排序后的数列为 。
若,显然有 ,所以我们可以令 。
若,显然有 ,所以我们可以令 。
所以一定会选择一段前缀和一段后缀。
假设至少存在一个数不属于前缀或后缀。
不失一般性地,我们设选择的前缀为 ,选择的后缀为 。
考虑将 替换为 ,此时答案会变化 $(1-x_{l})x_{l+1}+(1-x_{l+1})x_{i_{l+2}}-(1-x_{l})x_{i_{l+1}}-(1-x_{i_{l+1}})x_{i_{l+2}}=(1-x_l-x_{i_{l+2}})(x_{l+1}-x_{i_{l+1}})$,由于 ,所以当 时,替换不会更劣。
考虑将 替换为 ,此时答案会变化 $(1-x_{i_{k-r-1}})x_{m-r}+(1-x_{m-r})x_{m-r+1}-(1-x_{i_{k-r-1}})x_{i_{k-r}}-(1-x_{i_{k-r}})x_{m-r+1}=(1-x_{i_{k-r-1}}-x_{m-r+1})(x_{m-r}-x_{i_{k-r}})$,由于 ,所以当 时,替换不会更劣。
接下来只需证明 和 必有一个成立,那么我们就可以进行若干次调整,将不在前后缀上的数都放到前缀或后缀上,显然调整在有限步内一定结束。
显然有 和 必有一个成立。
若 ,由于 ,所以 成立。
若 ,由于 ,所以 成立。
在知道这两个性质后这道题就可以做了, 和 必选,维护当前选择的前缀和后缀,利用引理 2 中的式子判断当前应该扩大前缀还是后缀,由于选择相等的数不会改变 ,所以一次可以把相等的数全选了,复杂度瓶颈在排序 。
#include<cstdio> #include<algorithm> #include<functional> #include<utility> void solve(){ int n,k; scanf("%d %d",&n,&k); std::pair<long double,int> p[100010]; for(int i=1,x,y;i<=n;i++){ scanf("%d/%d %d",&x,&y,&p[i].second),p[i].first=1.*x/y; if(!p[i].second) --n,--i; } std::sort(p+1,p+n+1,std::greater()); if(k==1) return puts("0.000000"),void(); long double pl=p[1].first,pr=p[n].first; int l=1,r=n; k-=2,p[1].second--,p[n].second--; long double ans=0; while(k){ while(!p[l].second) ++l; while(!p[r].second) --r; int x; if(pl+pr>=1){ if(p[l].first==pl) x=std::min(k,p[l].second); else x=1; k-=x,p[l].second-=x; ans+=(1-pl)*p[l].first+(x-1)*(1-p[l].first)*p[l].first; pl=p[l].first; }else{ if(p[r].first==pr) x=std::min(k,p[r].second); else x=1; k-=x,p[r].second-=x; ans+=(1-p[r].first)*pr+(x-1)*(1-p[r].first)*p[r].first; pr=p[r].first; } } ans+=(1-pl)*pr; printf("%.6Lf\n",ans); } int main(){ int T; scanf("%d",&T); while(T--) solve(); return 0; }
- 1
信息
- ID
- 3067
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者