Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

CF739E Gosha is hunting

LuoGu: CF739E Gosha is hunting

CF: E. Gosha is hunting

Algorithm I 费用流

选择第一种球期望个数为 $p _ i$ ,选择第二种为 $u _ i$ ,同时选择为 $1 - (1 - p _ i) (1 - u _ i) = 1 - (1 - p _ i - u _ i + p _ i u _ i) = p _ i + u _ i - p _ i u _ i$ ,即在原来的答案上减少了 $p _ i u _ i$ ,由此可以构建处费用流模型。

  • 源点向两种球连边,容量为球的个数;
  • 两种球向所有的神奇宝贝连边,容量为 $1$ ,费用为 $p _ i$ 或 $u _ i$ ;
  • 所有神奇宝贝向汇点连边,容量为 $1$ 费用为 $0$ ,表示只选择一种球则不需要减少费用;容量为 $1$ ,费用为 $-p _ i u _ i$ 表示如果多了一个容量需要减少费用。
查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const double eps = 1e-10;
const int N = 2e3 + 10, M = 2e4 + 10, inf = 1e5;
bool vis[N];
int n, st, ed, A, B, pre[N], incf[N];
double p1[N], p2[N], d[N], f[M];
int idx = -1, hd[N], nxt[M], edg[M], wt[M];
bool spfa()
{
for (int i = st; i <= ed; i++)
d[i] = -inf;
d[st] = 0;
for (int i = st; i <= ed; i++)
incf[i] = 0;
incf[st] = inf;
queue<int> q;
q.push(st);
vis[st] = true;
while (!q.empty())
{
int t = q.front();
q.pop();
vis[t] = false;
for (int i = hd[t]; ~i; i = nxt[i])
if (d[t] + f[i] > d[edg[i]] + eps && wt[i])
{
d[edg[i]] = d[t] + f[i];
pre[edg[i]] = i;
incf[edg[i]] = min(wt[i], incf[t]);
if (!vis[edg[i]])
{
q.push(edg[i]);
vis[edg[i]] = true;
}
}
}
return incf[ed] > 0;
}
double ek()
{
double res = 0;
while (spfa())
{
int t = incf[ed];
res += t * d[ed];
for (int i = ed; i != st; i = edg[pre[i] ^ 1])
{
wt[pre[i]] -= t;
wt[pre[i] ^ 1] += t;
}
}
return res;
}
void add(int a, int b, int c, double d)
{
nxt[++idx] = hd[a];
hd[a] = idx;
edg[idx] = b;
wt[idx] = c;
f[idx] = d;
}
int main()
{
scanf("%d%d%d", &n, &A, &B);
st = 0, ed = n + 3;
for (int i = st; i <= ed; i++)
hd[i] = -1;
add(st, n + 1, A, 0);
add(n + 1, st, 0, 0);
add(st, n + 2, B, 0);
add(n + 2, st, 0, 0);
for (int i = 1; i <= n; i++)
scanf("%lf", &p1[i]);
for (int i = 1; i <= n; i++)
scanf("%lf", &p2[i]);
for (int i = 1; i <= n; i++)
{
add(n + 1, i, 1, p1[i]);
add(i, n + 1, 0, -p1[i]);
add(n + 2, i, 1, p2[i]);
add(i, n + 2, 0, -p2[i]);
add(i, ed, 1, 0);
add(ed, i, 0, 0);
add(i, ed, 1, -p1[i] * p2[i]);
add(ed, i, 0, p1[i] * p2[i]);
}
printf("%lf", ek());
return 0;
}

Algorithm II wqs二分

捕捉相同的神奇宝贝,使用的球越多,那么期望越大;但是增长速度却越慢。那么,这个函数满足凸性,可以使用凸优化,即 wqs二分

假设现在所有球可以随便用,那么此时期望值最大。如果我们减少球的权值,那么期望值相应减少。个数也不会无限了。我们需要二分一个恰当的减少的值,使得使用的个数和原来的相等时,凸函数函数值最大。

细节:最后答案应该用 $A$ 而不是 $cnta$ ,因为随着二分 $cnta$ 的值发生了改变。

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cstdio>
using namespace std;
const int N = 2e3 + 10;
const double eps = 1e-8;
int n, A, B, cnta, cntb;
double ans, u[N], v[N], w[N];
void check(double a, double b)
{
ans = cnta = cntb = 0;
for (int i = 1; i <= n; i++)
{
double mx = 0;
bool flaga = false, flagb = false;
if (u[i] - a > mx + eps)
{
mx = u[i] - a;
flaga = true, flagb = false;
}
if (v[i] - b > mx + eps)
{
mx = v[i] - b;
flaga = false, flagb = true;
}
if (w[i] - a - b > mx + eps)
{
mx = w[i] - a - b;
flaga = true, flagb = true;
}
ans += mx;
cnta += flaga, cntb += flagb;
}
}
int main()
{
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++)
w[i] = u[i] + v[i] - u[i] * v[i];
double la = 0, ra = 1, resa, resb;
while (la + eps < ra)
{
double mida = (la + ra) / 2;
double lb = 0, rb = 1;
while (lb + eps < rb)
{
double midb = (lb + rb) / 2;
check(mida, midb);
resb = midb;
if (cntb == B)
break;
cntb < B ? rb = midb : lb = midb;
}
resa = mida;
if (cnta == A)
break;
cnta < A ? ra = mida : la = mida;
}
printf("%lf", ans + A * resa + B * resb);
return 0;
}

UPD: 凸性似乎确凿是假的,但是在CF这样的hack机制下可以过,也确实说明了一些题目看似有凸性的直接用 WQS 二分可以获得一个不错的分数。