ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「NOI2016」国王饮水记(猜结论+动态规划+斜率优化)

2020-04-29 17:53:24  阅读:261  来源: 互联网

标签:int double db 斜率 NOI2016 ans qry 饮水 define


https://loj.ac/problem/2087

干脆把这年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";
}

标签:int,double,db,斜率,NOI2016,ans,qry,饮水,define
来源: https://www.cnblogs.com/coldchair/p/12803643.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有