2023牛客寒假算法基础集训营1 ACDEFGHKLM
A
题解
知识点:模拟。
显然。
(用char输入到一半直接给答案跳出,WA了两小时,无话可说。
时间复杂度 O(1)
空间复杂度 O(1)
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { string s; cin >> s; vector<int> sum(2); for (int i = 1;i <= 10;i++) { if (s[i - 1] == '1') sum[!(i & 1)]++; if (sum[0] > sum[1] + (10 - i + 1) / 2 || sum[1] > sum[0] + (10 - i) / 2) { cout << i << '\n'; return true; } } return false; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
C
题解
知识点:贪心。
显然只要不是 0 的论文只要不动就会有 1 的贡献,动了也至多有 1 的贡献不如不动。因此,记录非 0 论文数量即可。
时间复杂度 O(n)
空间复杂度 O(n)
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int a[100007]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) cin >> a[i]; int ans = 0; for (int i = 1;i <= n;i++) ans += a[i] != 0; cout << ans << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
D
题解
知识点:计算几何,贪心。
(px,py) 会有四个位置,矩阵内、上方、右方、右上方,分别进行讨论:
- px≤x,py≤y 在矩阵内部,比值最大值为 max 。
- p_x > x,p_y < y 在矩阵右方,比值最大值为 \max\left(\dfrac{xp_y}{xy+(p_x-x)p_y},\dfrac{x(y-p_y)}{xy+(p_x-x)(y-p_y)}\right) 。
- p_x < x,p_y > y 在矩阵上方,比值最大值为 \max\left(\dfrac{p_xy}{xy+p_x(p_y-y)},\dfrac{(x-p_x)y}{xy+(x-p_x)(p_y-y)}\right) 。
- p_x \geq x,p_y \geq y 在矩阵右上方,比值最大值为 \dfrac{xy}{p_xp_y} ,这个需要肉眼观察法qwq。
(手残把 y 写成 x ,又瞪了半小时。。。
时间复杂度 O(1)
空间复杂度 O(1)
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { int x, y, px, py; cin >> x >> y >> px >> py; if (px <= x && py <= y) { cout << fixed << setprecision(10) << 1.0 * max(px, x - px) * max(py, y - py) / x / y << '\n'; } else if (px > x && py < y) { double a = 1.0 * x * py / (x * y + py * (px - x)); double b = 1.0 * x * (y - py) / (x * y + (y - py) * (px - x)); cout << fixed << setprecision(10) << max(a, b) << '\n'; } else if (py > y && px < x) { double a = 1.0 * y * px / (x * y + px * (py - y)); double b = 1.0 * y * (x - px) / (x * y + (x - px) * (py - y)); cout << fixed << setprecision(10) << max(a, b) << '\n'; } else if (px >= x && py >= y) { cout << fixed << setprecision(10) << 1.0 * x * y / px / py << '\n'; } return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
E
题解
知识点:计算几何。
前两种操作是平移和旋转,并不会改变两条边的相对位置关系,而第三种操作可以通过镜像翻折改变位置关系。
先确定 ABC 和 DEF 靠左的一条边,然后比较长度,如果不同则一定用过第三种操作。
时间复杂度 O(1)
空间复杂度 O(1)
代码
#include <bits/stdc++.h> using ll = long long; using namespace std; using ld = long double; const ld pi = acos(-1.0L); const ld eps = 1e-8; template<class T> struct Point { T x, y; Point(const T &x = 0, const T &y = 0): x(x), y(y) {} Point(const Point &A, const Point &B): x(B.x - A.x), y(B.y - A.y) {} Point &operator+=(const Point &P) { x += P.x, y += P.y;return *this; } Point &operator-=(const Point &P) { x -= P.x, y -= P.y;return *this; } Point &operator*=(const T &k) { x *= k, y *= k;return *this; } Point &operator/=(const T &k) { x /= k, y /= k;return *this; } friend Point operator-(const Point &P) { return Point(-P.x, -P.y); } friend Point operator+(Point A, const Point &B) { return A += B; } friend Point operator-(Point A, const Point &B) { return A -= B; } friend Point operator*(Point P, const T &k) { return P *= k; } friend Point operator/(Point P, const T &k) { return P /= k; } bool operator==(const Point &P) const { return (abs(x - P.x) <= eps && abs(y - P.y) <= eps); } //点乘 friend T operator*(const Point &A, const Point &B) { return A.x * B.x + A.y * B.y; } //叉乘 friend T operator^(const Point &A, const Point &B) { return A.x * B.y - A.y * B.x; } //P的相对方向,逆时针为1 int toLeft(const Point &P) const { T cross = (*this) ^ P; return (cross > eps) - (cross < -eps); } //向量长度的平方 T len2() const { return (*this) * (*this); } //两点距离的平方 friend T dist2(const Point &A, const Point &B) { return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); } //*一定包含浮点的函数 //两点距离 friend ld dist(const Point &A, const Point &B) { return sqrt(dist2(A, B)); } //向量长度 ld len()const { return sqrt(len2()); } //向量夹角 friend ld ang(const Point &A, const Point &B) { return acos(max(-1.0L, min(1.0L, (A * B) / (A.len() * B.len())))); } //逆时针旋转rad Point rot(const ld rad) const { return { x * cos(rad) - y * sin(rad), x * sin(rad) + y * cos(rad) }; } //逆时针旋转参数 Point rot(const ld cosr, const ld sinr) const { return { x * cosr - y * sinr, x * sinr + y * cosr }; } }; bool solve() { ld xa, ya, xb, yb, xc, yc; cin >> xa >> ya >> xb >> yb >> xc >> yc; ld xd, yd, xe, ye, xf, yf; cin >> xd >> yd >> xe >> ye >> xf >> yf; Point<ld> BA({ xb,yb }, { xa,ya }); Point<ld> BC({ xb,yb }, { xc,yc }); Point<ld> ED({ xe,ye }, { xd,yd }); Point<ld> EF({ xe,ye }, { xf,yf }); if (BA.toLeft(BC) == 1) swap(BA, BC); if (ED.toLeft(EF) == 1) swap(ED, EF); if (abs(BA.len() - ED.len()) < eps) cout << "NO" << '\n'; else cout << "YES" << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
F
题解
知识点:并查集。
注意到,即便一个点放完炸弹不能走,但因为我们能控制放炸弹的顺序,炸弹是影响不了我们的路线的。所以实际上,只要炸弹点是连通的,我们就一定可以从这个连通块的任何点开始走,并且以任何点结束,答案便是这个连通块大小的平方。
当然如果炸弹点不在一个连通块就无解。
或者,没有炸弹点,我们就可以把所有连通块大小平方加起来。
用并查集维护连通性,以及连通块大小。
时间复杂度 O((m+n) \log n)
空间复杂度 O(n)
代码
#include <bits/stdc++.h> using ll = long long; using namespace std; struct DSU { vector<int> fa; vector<int> w; explicit DSU(int n):fa(n + 1), w(n + 1, 1) { for (int i = 1;i <= n;i++) fa[i] = i; } void init(int n) { for (int i = 1;i <= n;i++) fa[i] = i, w[i] = 1; } int find(int x) { if (fa[x] == x) return x; fa[x] = find(fa[x]); return fa[x]; }//按需修改 bool same(int x, int y) { return find(x) == find(y); } bool merge(int x, int y) { if (same(x, y)) return false; int px = fa[x], py = fa[y]; fa[px] = py; w[py] += w[px]; return true; }//按需修改;注意方向,x合并到y }; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; DSU dsu(n); for (int i = 1;i <= m;i++) { int u, v; cin >> u >> v; dsu.merge(u, v); } vector<int> c(n + 1); for (int i = 1;i <= n;i++) cin >> c[i]; bool ok = 1; int rt = 0; for (int i = 1;i <= n;i++) { if (c[i]) { if (rt) ok &= dsu.same(i, rt); else rt = dsu.find(i); } } if (!rt) { ll ans = 0; for (int i = 1;i <= n;i++) { if (i == dsu.find(i)) ans += 1LL * dsu.w[i] * dsu.w[i]; } cout << ans << '\n'; } else if (ok) cout << 1LL * dsu.w[rt] * dsu.w[rt] << '\n'; else cout << 0 << '\n'; return 0; }
G
题解
知识点:线段树,数学。
众所周知,分数是不会一直涨的qwq,所以 x_{i+1} = round(10\sqrt {x_i}) 在 x = 0,99,100 时会不变,这些点称为不动点。同时,其他点也会快速收敛到 99 或者 100 ,也就是说每个点最多修改几次就不用改了。
因此,可以写一个伪区间修改的线段树,即单点修改但套一个区间修改的壳子。然后再加一个区间值都为 99,100 时的优化,可以用最大值和最小值夹逼实现。单点修改时也是同样的,修改到 99,100 就可以结束了。
特判 a_i = 0 的情况,为了判断统一可以把这个节点的 max 和 min 的信息修改为 100 ,而 val 还是 0 ,不影响求和。
因为每个点最多修改几次可以看作常数,所以对单点修改次数是 O(n) ,所以复杂度也是 O(n\log n) 。
时间复杂度 O(n \log n)
空间复杂度 O(n)
代码
#include <bits/stdc++.h> using ll = long long; using namespace std; struct T { ll val; int mi, mx; static T e() { return T{ 0,0x3f3f3f3f,0 }; } friend T operator+(const T &a, const T &b) { return { a.val + b.val,min(a.mi,b.mi),max(a.mx,b.mx) }; } }; ///节点元封装类,定义单位元"e"、合并"+" struct F { int k; T operator()(const T &x) { int val = x.val; for (int i = 1;i <= k && val != 99 && val != 100;i++) { val = sqrt(val) * 10 + 0.5; } return T{ val,val,val }; } }; ///修改元封装类,定义映射"()" class SegmentTree { const int n; vector<T> node; void update(int rt, int l, int r, int L, int R, F f) { if (node[rt].mi >= 99 && node[rt].mx <= 100) return; if (r < L || l > R) return; if (l == r) { node[rt] = f(node[rt]); return; } int mid = l + r >> 1; update(rt << 1, l, mid, L, R, f); update(rt << 1 | 1, mid + 1, r, L, R, f); node[rt] = node[rt << 1] + node[rt << 1 | 1]; } T query(int rt, int l, int r, int x, int y) { if (l > y || r < x) return T::e(); if (x <= l && r <= y) return node[rt]; int mid = l + r >> 1; return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y); } public: SegmentTree(int _n):n(_n), node(_n << 2, T::e()) {} SegmentTree(int _n, vector<T> &src):n(_n), node(_n << 2, T::e()) { function<void(int, int, int)> build = [&](int rt, int l, int r) { if (l == r) { node[rt] = src[l]; return; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); node[rt] = node[rt << 1] + node[rt << 1 | 1]; }; build(1, 1, n); } void update(int L, int R, F f) { update(1, 1, n, L, R, f); } T query(int x, int y) { return query(1, 1, n, x, y); } }; ///线段树,建树O(nlogn)、修改查询O(logn),单点修改、区间查询 int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; vector<T> src(n + 1); for (int i = 1;i <= n;i++) { int x; cin >> x; if (x) src[i] = { x,x,x }; else src[i] = { 0,100,100 }; } SegmentTree sgt(n, src); while (m--) { int op; cin >> op; if (op == 1) { int l, r, k; cin >> l >> r >> k; sgt.update(l, r, { k }); } else { cout << sgt.query(1, n).val << '\n'; } } return 0; }
H
题解
知识点:数学。
因为面积总和不变,记录 n^2-1 多了或少了几个半圆,一定会出现在最后一块上,直接算即可。
时间复杂度 O(n^2)
空间复杂度 O(1)
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { int n; cin >> n; int ans = 10; for (int i = 1;i <= n * n - 1;i++) { for (int j = 1;j <= 4;j++) { char x; cin >> x; if (x == '1') ans++; else if (x == '2') ans--; } } cout << ans << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
K
题解
知识点:线性dp。
n\leq 2 特判为 0 。
设 dp[i][j][k] 表示为考虑了前 i 个位置,有 j 个 1 ,最后两个位置的状态是 k (00,10,01,11
四种状态)。
初值设置:
if (m >= 0) dp[3][0][0] = 0; if (m >= 1) dp[3][1][0] = dp[3][1][1] = dp[3][1][2] = 0; if (m >= 2) dp[3][2][1] = dp[3][2][2] = dp[3][2][3] = 1; if (m >= 3) dp[3][3][3] = 1;
转移方程:
dp[i][j][0] = min(dp[i - 1][j][0], dp[i - 1][j][1]); dp[i][j][1] = min(dp[i - 1][j][2], dp[i - 1][j][3] + 1); if (j >= 1) dp[i][j][2] = min(dp[i - 1][j - 1][0], dp[i - 1][j - 1][1] + 1); if (j >= 2) dp[i][j][3] = min(dp[i - 1][j - 1][2] + 1, dp[i - 1][j - 1][3] + 1);
本题实际可以贪心 O(n) 过,加点推公式可以 O(1) 过。
(公式写挂了,没考虑填满前的几个 1 的情况不符合规律要特判,于是用dp硬碾过去了qwq。
时间复杂度 O(nm)
空间复杂度 O(nm)
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int dp[1007][1007][4]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; if (n <= 2) { cout << 0 << '\n'; return 0; } memset(dp, 0x3f3f3f3f, sizeof dp); if (m >= 0) dp[3][0][0] = 0; if (m >= 1) dp[3][1][0] = dp[3][1][1] = dp[3][1][2] = 0; if (m >= 2) dp[3][2][1] = dp[3][2][2] = dp[3][2][3] = 1; if (m >= 3) dp[3][3][3] = 1; for (int i = 4;i <= n;i++) { for (int j = 0;j <= min(i, m);j++) { dp[i][j][0] = min(dp[i - 1][j][0], dp[i - 1][j][1]); dp[i][j][1] = min(dp[i - 1][j][2], dp[i - 1][j][3] + 1); if (j >= 1) dp[i][j][2] = min(dp[i - 1][j - 1][0], dp[i - 1][j - 1][1] + 1); if (j >= 2) dp[i][j][3] = min(dp[i - 1][j - 1][2] + 1, dp[i - 1][j - 1][3] + 1); } } cout << min({ dp[n][m][0],dp[n][m][1],dp[n][m][2],dp[n][m][3] }) << '\n'; return 0; }
L
题解
知识点:数学。
先团队,再个人,选择是独立的可以分开来算加起来。
团队选 1,2,3,4,5 次选到的概率都是 \dfrac{1}{5} ,特别地 5 才能选中次只选 4 次就可以结束了,期望 \dfrac{1+2+3+4+4}{5} 。
个人同理,期望为 \dfrac{1+2+3+3}{4} 。
总和为 5.05 ,选 32 。
(傻不拉几的真信了运气题,题都没看直接抽卡,抽了十几发。。
时间复杂度 O(1)
空间复杂度 O(1)
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cout << 32 << '\n'; return 0; }
M
题解
知识点:线性dp。
设 dp[i][j] 表示为考虑到倒数第 i 个人,还剩下 j 个仙贝。
初始状态 dp[0][0] = 0 。
枚举给了倒数第 i 个人 k 个仙贝的情况转移即可。
(傻不拉几的真信了找规律题,找了1小时屁都没发现。。。
时间复杂度 O(nm^2)
空间复杂度 O(nm)
代码
#include <bits/stdc++.h> #define ll long long using namespace std; double dp[507][507]; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; for (int i = 1;i <= n;i++) { for (int j = 0;j <= m;j++) { dp[i][j] = dp[i - 1][j]; for (int k = 1;k <= j;k++) { dp[i][j] = max(dp[i][j], dp[i - 1][j - k] + 1.0 * k / j); } } } cout << fixed << setprecision(10) << dp[n][m] << '\n'; return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17058803.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略