codeforces739e绝对的干货题解!!!!!

来源:互联网 发布:java session的用法 编辑:程序博客网 时间:2024/05/22 10:39

我先大致分析一下这个做法的背景然后具体做法在代码里解释。

这题网上的题解都感觉太差劲了目前为止是这样的。然后搞得我研究了很久,现在终于解决了。囧!!!!!

首先我们考虑所有期望由什么情况组成。

1.单独用a类型的球。2.单独用b类型的球。3 a类型和b类型一起用.

然后dp的话很容易想出一个o(n^3)的算法。。。f[n][a][b]前n个怪兽我们用a 个a球,b个b球得到的期望。太慢~~~~~~~虽然能过2333333333333.

之所以慢的原因是分量太多,那能不能合一起些呢?

比如(单独用a和a,b一起用算一类) (单独用b算一类)意思就是,a,b一起用的贡献算在a头上。

然后方程式 fa[n][a][b]不过还是(n^3)的。还是慢。。。那么能把b的单独用的贡献算在a头上吗,嗯,如果可以方程就是f[n][a],哈哈n^2了,但是有个问题,你哪能保证一定有那么多个b呢?前面我们n^3的时候单独开了位计数b的个数,现在木有了,b很阔能超限啊!!!,怎么办呢?重点来了,这个方法很妙啊,我们首先将b分成两份,一份是k,一份是b-k,这样减少,单独用b的方案产生的贡献,算在a头上的贡献,b-k是算在a头上的,这样导致了这个算在a头上的贡献是可调的,导致用b的数目也是可调的,试想如果k=0那么就是所有b的贡献都算在了a头上,这样固然很好(比如每个b都产生很大的贡献然后每次都用B球),但是木有那么多b球给你更新啊!!!!所以我们要调k的值啦,然后每固定一个k值你阔以把所有b球的值看成是b-k a球不变,然后每个b球的贡献都算在a头上,在这种情况下求出来的最大实际值是 f[n][a]+用的b球数*k。每个k值对应一个情况,k=0的时候就是原始情况,虽然这种情况来说,每个b木有减少,整体相对于其他情况一定是最好的,求出来的实际值也一定是最好的,但是我前面说过了,木有那么多b啊!!所以要二分k,k越小对应的情况越好,但是,b不一定有那么多,所以看着调吧。

  后记:我觉得我是现在网上写关于这题题解写的最好的淫了~~~~~233333333,还有这题还有一个改良的nlogn*logn的做法,不过不是我想出来的,当时对于这题的认识还木有那么清晰,要是现在,肯定能想出来啦。。。毕竟就是把a做同样的操作而已。。。

//由于这个不是我写的所以我感觉代码有点丑。。。但是能解释清楚#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;const int N = 2010;int n, a, b;double p[N], u[N], q[N];double f[N][N]; int g[N][N];#define eps 1e-9#define nowf (f[i][j])#define nowg (g[i][j])int dcmp(double x){if (fabs(x)<eps)return 0;return ((x>0) ? 1 : -1);}void up(double &f1, int &g1, double f2, int g2){if (f2>f1 + eps){ f1 = f2; g1 = g2; }}int get(double K){for (int i = 1; i <= n; ++i)for (int j = 0; j <= a; ++j){nowf = nowg = 0;up(nowf, nowg, f[i - 1][j], g[i - 1][j]);//这里什么球都没用up(nowf, nowg, f[i - 1][j] + u[i] - K, g[i - 1][j] + 1);//这里用的是b球因为(b-k)是从b球类分出来的所以用了(b-k)相当于用bif (j)up(nowf, nowg, f[i - 1][j - 1] + p[i], g[i - 1][j - 1]);//用a球         if (j)up(nowf, nowg, f[i - 1][j - 1] + q[i] - K, g[i - 1][j - 1] + 1);//用a球和b球这里应该写成f+p[i]+(u[i]-k)+p[i]*u[i]就好理解}return g[n][a];}int main(){scanf("%d%d%d", &n, &a, &b);for (int i = 1; i <= n; ++i)scanf("%lf", p + i);for (int i = 1; i <= n; ++i)scanf("%lf", u + i);for (int i = 1; i <= n; ++i)q[i] = p[i] + u[i] - p[i] * u[i];double l = -1e4, mid, r = 1e4;while (r - l>eps){mid = (l + r)*0.5;//二分k值if (get(mid)<b)r = mid;else l = mid;}printf("%lf\n", f[n][a] + l*b);return 0;}

嗯,上面的算法还是太慢啦23333.。。1000ms+

既然阔以分b那么为什么不能分a呢,嗯分a后就能达到nlogn*logn了。。。快的飞起!!!!30ms+

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>using namespace std;double u[3000], v[3000], p[3000];double expect[3000];int n, a, b;int numa[3000], numb[3000];void test(double aa, double bb){for (int i = 1; i <= n; i++){double d1 = u[i] - aa;double d2 = v[i] - bb;double d3 = p[i] - aa - bb;double d4 = 0;numa[i] = numa[i - 1];numb[i] = numb[i - 1];expect[i] = expect[i - 1];if (d1 >= d2&&d1 >= d3&&d1 > d4)//比较顺序,和大于等于号是有讲究的。。{                                 //尽量少用d3,d1和d2在相等时随便选numa[i] += 1;expect[i] += d1;continue;}if (d2 >d1&&d2 >= d3&&d2 >d4){numb[i]+= 1;expect[i]+= d2;continue;}if (d3 > d1&&d3 > d2&&d3 > d4){numb[i] += 1;numa[i] += 1;expect[i] +=d3;}if (d4 >= d1&&d4 >= d3&&d4 >= d2)continue;}}int main(){//double lllll = 0.000000000000000001;//cout << lllll <<endl;scanf("%d%d%d", &n, &a, &b);for (int i = 1; i <= n; i++){scanf("%lf", &u[i]);}for (int i = 1; i <= n; i++)scanf("%lf", &v[i]);for (int i = 1; i <= n; i++)p[i] = u[i] + v[i] - u[i] * v[i];double ll = 0, rr = 1;double l, r;while (rr - ll > 1e-12){double mida = (ll + rr) / 2; l = 0, r = 1;while (r - l > 1e-12){double midb = (l + r) / 2;test(mida, midb);if (numb[n] < b)r = midb;elsel = midb;if (numb[n] == b)break;//同下}if (numa[n] < a)rr = mida;elsell = mida;if (numa[n] == a)break;//如果numa[n]==a的时候此时已经是极限了如果不break 然后ll=mida然后mida上升导致所得解偏小。}//cout << "ll:" << ll << " " << "l:" <<l << endl;//cout << ll*numa[n] + l*numb[n] << endl;//cout << numa[n] << " " << numb[n] << endl;//cout << expect[n] << endl;printf("%.5lf", expect[n] + numa[n]*ll + numb[n]*l);//注意这得写法不是 expect[n] + a*ll + b*l}





阅读全文
0 0