10.16 NOIP模拟赛 期望斜率优化 + 模拟 + 状压
来源:互联网 发布:js面向对象的思想 编辑:程序博客网 时间:2024/06/08 12:16
游戏 (game.cpp/c/pas)
【题目描述】
有n 个数,编号从1 到n。现在把n 个数分成k 组编号为1 到k,使得每组内的数必须
连续,组与组之间不能相交并且每个数必须属于一个组。
游戏进行的过程如下:
1. 如果n 个数都已经获得了,游戏结束。否则,找到编号最小没有全部获得的组X。
2. 游戏系统会给一个空的盒子,对于组X 中已经获得的数i,将ti 张写着数i 的卡片放
入盒子中,对于组X 中最小的没有获得的数j,将tj 张写着数j 的卡片放入盒子中。
3. 随机从盒子中抽取一张卡片,表示当前获得的数字,然后等待1 小时的冷却时间后
跳转到过程1。
现在需要确定一个最好的分组,使得这个游戏期望结束的时间最小。
【输入格式】
第一行两个整数n,k。
第二行n 个整数,第i 个整数为ti。
【输出格式】
一行,游戏结束的最小期望时间,保留小数点后两位。
【样例输入】
4 2
100 3 5 7
【样例输出】
5.74
【数据范围】
对于30%的数据, 1 <= n <= 20。
对于100%的数据,1 <= n <= 200000, 1 <= k <= min(50, n),1 <= ti <= 100000。
【样例解释】
分组方式为{100},{3,5,7}。
这道题一开始感觉很难的样子, 不过仔细推算一下发现是非常naive的斜率优化.
首先对于每一段中的一个i来说, 每一个i的贡献是i到这一段开头的sum, 由于每一次花费是1h, 所以期望贡献是 sum / ti(每次是1h, 抽到的概率是 ti / sum, 所以1/(ti/sum)就是期望抽到的时间). 假设j+1到i是一段, 那么这一段每一个的贡献就是:
sigma (sum[x1] - sum[j]) / t[x1] (j+1<=x1<=i)
sum[x1] 可以用一个s1数组维护出来, sum[j] / t[x1]也可以维护一个1/t[x1]的前缀和. 乘以sum[j]就得到了要减的数.
所以
dp[i] = dp[j] + s1[i] - s1[j] - (s2[i] - s2[j]) * sum[j];
斜率优化即可.
#include<stdio.h>const int maxn = 200005;int n, k, q[maxn];double dp[maxn][51], sum[maxn], s1[maxn], s2[maxn], t[maxn];inline double up(int j1, int k1, int p, int i){ return dp[j1][p - 1] - dp[k1][p - 1] - s1[j1] + s1[k1] + s2[j1] * sum[j1] - s2[k1] * sum[k1];}inline double down(int j1, int k1){ return sum[j1] - sum[k1];}int main(){ freopen("game.in", "r", stdin); freopen("game.out", "w", stdout); scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i){ scanf("%lf", &t[i]); sum[i] = t[i] + sum[i - 1]; s1[i] = s1[i - 1] + sum[i] / t[i]; s2[i] = s2[i - 1] + 1.0 / t[i]; } for(int i = 1; i <= n; ++i) dp[i][1] = s1[i]; for(int j = 2; j <= k; ++j){ int h = 1, t = 0; q[++t] = 0; for(int i = 1; i <= n; ++i){ while(h < t && up(q[h + 1], q[h], j, i) <= s2[i] * down(q[h + 1], q[h])) ++h; dp[i][j] = dp[q[h]][j - 1] + s1[i] - s1[q[h]] - (s2[i] - s2[q[h]]) * sum[q[h]]; while(h < t && up(q[t - 1], q[t], j, i) * down(q[t], i) >= up(q[t], i, j, i) * down(q[t - 1], q[t])) --t; q[++t] = i; } } printf("%0.2lf\n", dp[n][k]);}
开关灯 (lamp.cpp/c/pas)
【题目描述】
有n个灯,初始时都是不亮的状态,每次你可以选择一个某一个灯,不妨记为x,所有满足和x距离不超过k的灯的状态都将被翻转,选择第i个灯的代价记为c[i],问最终所有灯都是亮的状态的最小花费。
【输入格式】
输入有两行,第一行包含两个正整数n , k。
第二行包含n个整数,分别表示c[i]。
【输出格式】
一行,表示最小花费。
【样例输入】
3 1
1 1 1
【样例输出】
1
【数据范围】
20%的数据保证:1 <= N <= 20。
100%的数据保证:1 <= N <= 10000 , 0 <= k <= 1000 , 0 <= c[i] <= 1000000000。
很明显当前选的不能跟之前选的有重叠.
O(n)模拟即可.
#include<stdio.h>#include<cstring>#include<algorithm>using namespace std;typedef long long dnt;const int maxn = 10005;int n, k;dnt f[maxn], c[maxn], ans;int main(){ freopen("lamp.in", "r", stdin); freopen("lamp.out", "w", stdout); ans = -1; memset(f, -1, sizeof(f)); scanf("%d%d", &n, &k); for(int i = 1; i <= n; ++i) scanf("%I64d", &c[i]); for(int i = 1; i <= n; ++i){ if(i - k <= 1) f[i] = c[i]; if(i - 2 * k - 1 >= 1) if(~f[i - 2 * k - 1]) f[i] = f[i - 2 * k - 1] + c[i]; if(~f[i] && i + k >= n){ if(ans == -1) ans = f[i]; else ans = min(ans, f[i]); } } printf("%I64d\n", ans);}
工作 (work.pas/cpp/c)
【题目描述】
有N件事,每件事有两个属性a,b,现在你要以某种顺序做完这N件事,考虑这个人目前做的事情是i,他做的前一件事是j,那么他做这件事的代价就是(a[i] | a[j]) – (a[i] & a[j]),如果前面没有做事,那么代价就是a[i],但是事情总有轻重缓急之分,按原本顺序的事i最多能推迟到做完任意件紧接着事i之后的事j,i < j <= i + b[i]后做,即原本顺序的事k,k > i + b[i] ,不能在事i之前完成。
【输入格式】
输入的第一行是一个整数N,表示要做N件事。
接下来N行,每行两个数,表示 a[i]和b[i]。
【输出格式】
输出一个整数表示最小可能的最大传递时间
【样例输入】
2
5 1
4 0
【样例输出】
5
【数据范围】
20%的数据保证:1 <= N <= 15。
100%的数据保证:1 <= N , a[i] <= 1000 , 1 <= b[i] <= 7。
f(i,j,k)表示前i-1人都吃过饭,j表示i与i之后7人的吃饭情况,k表示上一个吃饭的人与i的相对位置
转移
若j&1==1
f(i,j,k)->f(i+1,j>>1,k-1)
否则枚举j集合内没吃饭的人
f(i,j,k)+cal(i+k,i+l)->f(i,j+bin[l],l).
dp刷表法更新状态.
#include<stdio.h>#include<algorithm>using namespace std;const int inf = 1e9;const int maxn = 1005;int n, ans;int a[maxn], b[maxn], f[maxn][(1 << 8)][16], pw[9];int calc(int x, int y){ if(!x) return 0; return (a[x] | a[y]) - (a[x] & a[y]);}int main(){ freopen("work.in", "r", stdin); freopen("work.out", "w", stdout); ans = inf; pw[0] = 1; for(int i = 1; i <= 20; ++i) pw[i] = pw[i - 1] << 1; scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d%d", &a[i], &b[i]); for(int i = 1; i <= n + 1; ++i) for(int j = 0; j < pw[8]; ++j) for(int k = 0; k <= 15; ++k) f[i][j][k] = inf; f[1][0][7] = 0; for(int i = 1; i <= n; ++i) for(int j = 0; j < pw[8]; ++j) for(int k = 0; k <= 15; ++k) if(f[i][j][k] < inf){ if(j & 1) f[i + 1][j >> 1][k - 1] = min(f[i + 1][j >> 1][k - 1], f[i][j][k]); else{ int lim = inf; for(int l = 0; l <= 7; ++l) if(!(pw[l] & j)){ if(i + l > lim) break; lim = min(lim, i + l + b[i + l]); f[i][j + pw[l]][l + 8] = min(f[i][j + pw[l]][l + 8], f[i][j][k] + calc(i + l, i + k - 8)); } } } for(int i = -8; i <= -1; ++i) ans = min(ans, f[n + 1][0][i + 8]); printf("%d\n", ans);}
今日MLE了…啊被罚唱歌.
- 10.16 NOIP模拟赛 期望斜率优化 + 模拟 + 状压
- 【期望 二分】【noip模拟赛】比赛
- 【数学期望】【NOIP模拟赛】连续段的期望
- #NOIP模拟赛bzoj3449#大佬(期望好题)
- NOIP模拟题 期望DP 礼物
- [noi模拟赛]math (斜率优化,未测)
- noip模拟赛 双城记
- 【noip模拟赛】密码
- 10.10NOIP模拟赛
- 10.08NOIP模拟赛
- 10.11NOIP模拟赛
- 10.12NOIP模拟赛
- 10.13NOIP模拟赛
- 【NOIP模拟赛】小奇挖矿
- NOIP模拟赛--军训
- 【NOIP模拟赛】数列
- noip模拟赛day8
- 16.1117 NOIP 模拟赛
- 查看python所支持的wheel包
- java常考面试题
- Python入坑第一章——基础篇
- 学习Git使用
- liunx实验二
- 10.16 NOIP模拟赛 期望斜率优化 + 模拟 + 状压
- Wannafly挑战赛1 B
- 进程间通信IPC---共享内存
- Leetcode#15. 3Sum
- UVA
- HTTPS理解
- node.js环境变量配置
- 高版本模拟器不能访问Data的问题解决方案
- 一:邮件开发模版