HGOI 20190714
最后一题各种坑爹玩野,被坑到80
还好没有像某奕一样被坑到40嘿嘿
整体海星,加油
T1 淘汰赛制(elimination)
很明显是概率 \(dp\)(其实可以模拟,而且很多人过了)
\(dp[i][j]\) 表示第 \(i\) 轮 \(j\) 还存活的概率
转移:$dp[i][j] = dp[i - 1][j] * \sum\limits_{k 是它可能遇到的} f[i - 1][k] * a[j][k] $
\(k\) 是一个区间, 但是必须满足经过 \(i\) 轮之后与 \(j\) 成为对手
这样的 \(k\) 满足一个一个性质,即 \(id(k)=id(j)±1, id(j) = (j-1)/2^{(i-1)}+1\)
\(±1\) 根据 \(id(j)\) 的奇偶而定
然后在 \(dp[n][i]\) 找 \(max\) 即可
#include<bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
using namespace std;
const int N = 1 << 11 ;
int k, n, m, stp, ans ;
int a[N][N] ;
double f[N][N], Max ;
// f[i][j] 表到第 i 轮 j 获胜的概率
// f[i][j] = f[i - 1][j] * sum(f[i - 1][k] * a[j][k])
int id(int x) {
return (x - 1) / stp + 1 ;
}
signed main() {
freopen("elimination.in", "r", stdin) ;
freopen("elimination.out", "w", stdout) ;
scanf("%d", &n) ;
rep(i, 1, (1 << n))
rep(j, 1, (1 << n))
scanf("%d", &a[i][j]) ;
rep(j, 1, (1 << n)) f[0][j] = 1 ;
stp = 1 ;
rep(i, 1, n) {
rep(j, 1, (1 << n)) {
double s = 0 ;
f[i][j] = f[i - 1][j] ;
int curid = id(j), beat ;
if (curid & 1) beat = curid + 1 ;
else beat = curid - 1 ;
rep(k, 1, (1 << n)) if (id(k) == beat) s += f[i - 1][k] * a[j][k] / 100 ;
f[i][j] *= s ;
}
stp *= 2 ;
}
rep(i, 1, (1 << n))
if (f[n][i] > Max) {
Max = f[n][i] ; ans = i;
}
printf("%d\n", ans) ;
return 0 ;
}
T2 种树(trees)
这题相对简单
首先对要求按 \(l\) 从小到大排序
然后考虑每个要求
对于一个操作,我们要将其完成,且其能够尽可能多的完成后面的要求
因此我们从后往前放树
时间复杂度 \(O(wh)\)
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
const int N = 100010 ;
int n, m, ans ;
int v[N] ;
struct node {
int l, r, t ;
bool operator < (const node &rhs) const {
return r == rhs.r ? l < rhs.l : r < rhs.r ;
}
} a[N] ;
signed main() {
freopen("trees.in", "r", stdin) ;
freopen("trees.out", "w", stdout) ;
scanf("%d%d", &n, &m) ;
rep(i, 1, m) scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].t) ;
sort(a + 1, a + m + 1) ;
// rep(i, 1, m) printf("%d %d %d\n", a[i].l, a[i].r, a[i].t) ;
rep(i, 1, m)
if (a[i].t > 0) {
int pos = a[i].r ;
ans += a[i].t ;
rep(j, 1, a[i].t) {
while (v[pos]) pos-- ;
v[pos] = 1 ;
rep(k, i + 1, m) if (a[k].l <= pos && pos <= a[k].r) a[k].t-- ;
}
}
printf("%d\n", ans) ;
return 0 ;
}
T3 软件开发(software)
一眼就二分
二分答案,我们只需要考虑能否实现
\(f[i][j]\) 表示前 \(i\) 个人做了 \(j\) 个任务 \(1\) 的模块后还能做的任务 \(2\) 的模块数
转移方程: \(f[i][j] = max(f[i][j], f[i - 1][k] + (x - (j - k) * d1[i]) / d2[i])\)
如果 \(f[n][m] >= m\) 答案就可以实现
注意两个坑点:
- 开始要全部赋 \(-∞\)
- 二分右边界不能太大,不然 \(dp\) 会爆 \(int\)
时间复杂度 \(O(N^3)\)
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
const int iinf = 0x3f3f3f3f ;
const int N = 1010 ;
int f[N][N] ; // f[i][j] 表示前i个人做了j个任务1的模块后还能做的任务2的模块数
int n, m, ans ;
int d1[N], d2[N] ;
bool check(int x) {
memset(f, -1, sizeof(f)) ;
f[0][0] = 0 ;
// rep(i, 1, m) f[0][i] = -iinf ;
rep(i, 1, n)
rep(j, 0, m)
rep(k, 0, j)
if (x >= (j - k) * d1[i] && f[i - 1][k] != -1)
f[i][j] = max(f[i][j], f[i - 1][k] + (x - (j - k) * d1[i]) / d2[i]) ;
// rep(i, 1, n) {
// rep(j, 0, m) cout << f[i][j] << " " ;
// cout << endl ;
// }
return f[n][m] >= m ;
}
signed main() {
// freopen("software.in", "r", stdin) ;
// freopen("software.out", "w", stdout) ;
scanf("%d%d", &n, &m) ;
rep(i, 1, n) scanf("%d%d", &d1[i], &d2[i]) ;
int l = 0, r = 20000, ans = 0 ;
while (l <= r) {
int mid = (l + r) >> 1 ;
if (check(mid)) ans = mid, r = mid - 1 ;
else l = mid + 1 ;
}
// ans = 0 ;
// rep(i, max(0, l - 5), min(iinf, r + 5))
// if (check(i)) {
// ans = i ;
// break ;
// }
// check(15) ;
printf("%d\n", ans) ;
return 0 ;
}
加油ヾ(◍°∇°◍)ノ゙