2.14 寒假练习赛Round 2
T1 NKOJ 3677 观光车
何老板带领n名游客来到一景区大门口,需要乘坐观光车游览景区。
景区提供两种观光车,一种是每辆车可以坐a名游客,包一辆车费用是p1块钱;另一种每辆车可以坐b名游客,包一辆车费用是p2块钱。
何老板想让这n名游客都坐上观光车,且每辆车都坐满。问何老板至少要花费多少钱?
思路:首先尽可能多用性价比高的车,然后再不断调整两种车的数量,让性价比高的车最多即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } main() { ll n, x, y, a, b; scanf("%lld%lld%lld%lld%lld", &n, &x, &a, &y, &b); if (n % gcd(a, b)) return puts("-1"), 0; ll k = gcd(a, b); if (x * b > y * a) swap(x, y), swap(a, b); a /= k, b /= k, n /= k; for (ll i = n / a; i; i--) { if ((n - a * i) % b == 0) { ll j = (n - a * i) / b; printf("%lld", x * i + y * j); return 0; } } puts("-1"); }
T2 NKOJ 3950 越狱
监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种。如果
相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱
思路:反向思考,已知所有的方案数,那么考虑所有相邻房间的犯人的宗教均不相同的方案数,二者相减即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 100003; inline ll KSM(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) (ans *= a) %= mod; (a *= a) %= mod, b >>= 1; } return ans; } main() { ll m, n; scanf("%lld%lld", &m, &n); ll x = KSM(m, n), y = m * KSM(m - 1, n - 1) % mod; printf("%lld", (x - y + 2 * mod) % mod); }
T3 NKOJ 2044 奶牛政坛
建议自行看题。
思路:可以证明,n个点中任两点的距离的最大值,一定是这n个点中深度最大的那个点和其他点的距离的最大值(不知道讲清楚没有)求距离直接lca即可。
#include <bits/stdc++.h> using namespace std; struct node { int to, nxt; }e[400005]; int tot, head[200005], fa[200005][30], dep[200005], a[200005], maxd[200005], ans[200005]; inline void add_e(int u, int v) {e[++tot].to = v; e[tot].nxt = head[u]; head[u] = tot;} void dfs(int x) { for (int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if (y != fa[x][0]) { fa[y][0] = x; dep[y] = dep[x] + 1; dfs(y); } } } inline int lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); int k = dep[x] - dep[y]; for (int i = 0; i <= 24; i++) { if (k & (1 << i)) x = fa[x][i]; } if (x == y) return x; for (int i = 24; ~i; i--) { if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; } return fa[x][0]; } int main() { int n, k, rt; scanf("%d%d", &n, &k); for (int i = 1, j; i <= n; i++) { scanf("%d%d", &a[i], &j); if (!j) rt = i; add_e(i, j), add_e(j, i); } dfs(rt); for (int j = 1; j <= 24; j++) for (int i = 1; i <= n; i++) fa[i][j] = fa[fa[i][j - 1]][j - 1]; for (int i = 1; i <= n; i++) { if (dep[maxd[a[i]]] < dep[i]) maxd[a[i]] = i; } for (int i = 1; i <= n; i++) { int dis = dep[i] + dep[maxd[a[i]]] - 2 * dep[lca(i, maxd[a[i]])]; ans[a[i]] = max(ans[a[i]], dis); } for (int i = 1; i <= k; i++) { printf("%d\n", ans[i]); } }
T4(订正) NKOJ 4914 下载讲义
信竞期末考试要到了,你想要进行全面的复习。你来到机房,打算从网盘下载这学期何老板发的所有讲义。
共有n份讲义(编号1到n)需要下载。机房一共有m台(编号1到m)电脑,每台电脑都可使用,但一开始所有电脑都处于关机状态。
你事先已经得知:
电脑的开机耗时可能不同,第i台电脑开机需要Si秒钟。
同一份讲义,用不同的电脑下载,耗时可能不同。用第i台电脑下载第j份讲义,需要花费Dij秒。
你需要计算,下载完所有讲义,最少需要多少秒钟(所有电脑耗时总和最少)。
思路:这个题确实棋差一着,其实可以设f[i][s]为用(不一定开每一台)前i台电脑,下载讲义情况为s所用时间的最小值,有方程f[i][s | 1 << k - 1] = min(f[i][s | 1 << k - 1], f[i][j] + a[i][k]),直接切。
#include <bits/stdc++.h> using namespace std; int a[105][20], f[105][(1 << 16) + 1], s[105]; int main() { int m, n; scanf("%d%d", &m, &n); int tot = (1 << n) - 1; for (int i = 1; i <= m; i++) { scanf("%d", &s[i]); for (int j = 1; j <= n; j++) { scanf("%d", &a[i][j]); } } memset(f, 0x3f, sizeof f); f[0][0] = 0; for (int i = 1; i <= m; i++) { for (int j = 0; j <= tot; j++) { f[i][j] = f[i - 1][j] + s[i]; } for (int k = 1; k <= n; k++) { for (int j = 0; j <= tot; j++) { if (!(j & (1 << k - 1))) { f[i][j | (1 << k - 1)] = min(f[i][j | (1 << k - 1)], f[i][j] + a[i][k]); } } } for (int j = 0; j <= tot; j++) f[i][j] = min(f[i - 1][j], f[i][j]); } printf("%d", f[m][tot]); }
T5 NKOJ 5617 黑白球
何老板将2N个小球排成一排。其中有N个白球和N个黑球。
白球编号1到N,每个白球上都有一个数字,表示它的编号。
黑球编号1到N,每个黑球上都有一个数字,表示它的编号。
可以任意交换两个相邻小球,何老板想知道,最少几次交换就能满足下列要求:
对于任意的(i,j), 1 ≤ i < j ≤ N,编号i的白球一定在编号j的白球左侧
对于任意的(i,j), 1 ≤ i < j ≤ N,编号i的黑球一定在编号j的黑球左侧
简单的说,最少进行几次交换,就能使得编号小的白球一定出现在编号大的白球的左侧,编号小的黑球一定出现在编号大的黑球的左侧。
思路:dp,设f[i][j]为摆前i个白球,前j个黑球的最小代价,有方程
f[i][j] = min(f[i][j], f[i - 1][j] + costw[i - 1][j]), f[i][j] = min(f[i][j], f[i][j - 1] + costb[i][j - 1])
预处理出cost数组即可。
#include <bits/stdc++.h> using namespace std; int w[2005], b[2005], valw[2005][2005], valb[2005][2005], f[2005][2005]; int main() { int n; char ch; scanf("%d", &n); for (int i = 1, x; i <= 2 * n; i++) { cin >> ch; scanf("%d", &x); if (ch == 'W') w[x] = i; else b[x] = i; } for (int i = 0; i < n; i++) { for (int j = 1; j <= i; j++) { valw[i][0] += (w[j] > w[i + 1]); } for (int j = 1; j <= n; j++) { valw[i][j] = valw[i][j - 1] + (b[j] > w[i + 1]); } } for (int j = 0; j < n; j++) { for (int i = 1; i <= j; i++) { valb[0][j] += (b[i] > b[j + 1]); } for (int i = 1; i <= n; i++) { valb[i][j] = valb[i - 1][j] + (w[i] > b[j + 1]); } } memset(f, 0x3f, sizeof f); f[0][0] = 0; for (int i = 0; i <= n; i++) { for (int j = 0; j <= n; j++) { if (i) f[i][j] = min(f[i][j], f[i - 1][j] + valw[i - 1][j]); if (j) f[i][j] = min(f[i][j], f[i][j - 1] + valb[i][j - 1]); } } printf("%d\n", f[n][n]); }

浙公网安备 33010602011771号