干脆把这年NOI改成猜结论大赛好了。
先把(h)从小到大排序。
上来先猜三个显然的结论:
1.一个(h[i])不会被用多次
2.我们用到的一定是(h)的一个后缀
3.(h)的后缀用法,把(h)的后缀划分成若干段,从左往右每次合并一段
设(f[i][j])表示合并了(i)次,用了(j)以前的最大答案。
设(s)表示(h)的前缀和,则转移:
$ f[i][j]=max(frac{f[i-1][k]-s[k]+s[i]}{i-k+1})$
暴力就有60分了。
考虑这个相当于二维平面上若干点,要求一个点P和这些点连线的最大斜率。
4.看起来有决策单调性,所以可以用分治优化转移,应该有70分了。
5.答案点一定在凸包上,对于这题,答案点一定在上凸壳,可以在上面三分(二分)。
结合,4,5,可以得到单调队列维护凸壳的做法,复杂度:(O(nkp)),可以获得85分,用double做后面的,可以获得91分。
接着开始非人类:
由(h)互不相同性质:
6.前一段长度一定大于等于后一段的长度。
7.长度大于1的区间只有(O(log frac{nh}{H})),*这个我已经感受不到正确性了。
于是只做14次dp,剩下的一次扫一遍求最优。
中间可以用double算来卡常数。
Code(除掉高精度模板):
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;
#define db Decimal
const int N = 8005;
int n, k, p;
int h[N];
db f[N], g[N], s[N];
struct P {
int x; double y;
P(){}
P(int _x, double _y) {
x = _x, y = _y;
}
};
P operator - (P a, P b) {
return P(a.x - b.x, a.y - b.y);
}
db operator ^ (P a, P b) {
return a.x * b.y - a.y * b.x;
}
P z[N]; int L, R;
void add(P a) {
while(L < R && ((a - z[R]) ^ (z[R] - z[R - 1])) >= 0) R --;
z[++ R] = a;
}
db qry(P a, P b) { return (b.y - a.y) / (b.x - a.x);}
int qry(P a) {
while(L < R && qry(z[L + 1], a) > qry(z[L], a)) L ++;
return z[L].x + 1;
}
int main() {
freopen("a.in", "r", stdin);
scanf("%d %d %d", &n, &k, &p);
fo(i, 1, n) scanf("%d", &h[i]);
sort(h + 2, h + n + 1);
if(k >= n) {
db ans = db(h[1]);
fo(i, 2, n) {
if((db) h[i] > ans)
ans = (ans + h[i]) / 2;
}
cout << ans.to_string(p) << "
";
return 0;
}
fo(i, 2, n) s[i] = s[i - 1] + db(h[i]);
fo(i, 1, n) f[i] = db(h[1]);
fo(t, 1, min(14, k)) {
L = 1, R = 0;
fo(i, 1, n) {
g[i] = f[i];
add(P(i - 1, (s[i] - f[i]).to_double()));
int x = qry(P(i, s[i].to_double()));
f[i] = max(f[i], (g[x] + s[i] - s[x]) / (i - x + 1));
}
}
db ans = db(0);
if(k <= 14) {
fo(i, 2, n) ans = max(ans, f[i]);
} else {
int st = max(1, n - (k - 14));
ans = db(f[st]);
fo(i, st + 1, n) {
ans = (ans + h[i]) / 2;
ans = max(ans, f[i]);
}
}
cout << ans.to_string(p) << "
";
}