「NOI2016」国王饮水记(猜结论+动态规划+斜率优化)
干脆把这年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("\n")
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) << "\n";
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) << "\n";
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址