2023牛客寒假算法基础集训营1
2023牛客寒假算法基础集训营1
https://ac.nowcoder.com/acm/contest/46800
过了7题,写一半没撑住去睡觉了。
官方难度预期:
签到:ALCH
简单:DKM
中等:GFE
困难:BIJ
果然我还是很菜哇qaq
补了一下题发现确实GFE不难,其中F需要阅读理解细心一点,E计算几何需要debug的耐心,G需要有手玩几个样例找规律的耐心。
B学到前缀和优化dp。剩下两题一个是不太会实现,一个是不太懂题姐
总结: 沉下心来多想想,别急。
A - World Final? World Cup! (I)
模拟
#include <bits/stdc++.h>
using namespace std;
void solve() {
string s;
cin >> s;
s = ' ' + s;
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= 10; i++) {
if (i & 1) cnt1 += s[i] == '1';
else cnt2 += s[i] == '1';
if (cnt1 == cnt2) continue;
int dx = abs(cnt1 - cnt2), add = (10 - i) / 2;
if (i & 1) {
if (cnt2 < cnt1) add ++;
}
if (add < dx) {
cout << i << endl;
return;
}
}
//cout << cnt1 << ' ' << cnt2 << endl;
cout << -1 << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
}
B - World Final? World Cup! (II)
非常好题姐:https://www.cnblogs.com/fried-chicken/p/13736254.html
学到了前缀和优化dp的思想
还是稍微复杂一点就不会设状态啊悲。
#include <bits/stdc++.h>
using namespace std;
const int N = 45 * 3, M = 105, mod = 998244353;
int n, m, x, y, ans;
int f[N][M][M]; //f[i][j][s][t]: 比完i场, 1总分j, 1赢了s次, 2赢了t次
int up[N][M][M], mid[N][M][M], down[N][M][M]; //上三角, 对角线, 下三角的dp前缀和
int main () {
cin >> n >> m >> x >> y;
f[0][0][0] = 1;
for (int i = 1; i <= n; i++) { //滚掉一维
for (int j = 0; j <= 3 * n; j++) { //预处理前缀和
//init
for (int u = 0; u <= x; u++) down[j][u][0] = mid[j][u][0] = f[j][u][0];
for (int v = 0; v <= y; v++) up[j][0][v] = mid[j][0][v] = f[j][0][v];
//prefix-mid
for (int u = 1; u <= x; u++) {
for (int v = 1; v <= y; v++) {
mid[j][u][v] = (mid[j][u-1][v-1] + f[j][u][v]) % mod;
}
}
//prefix-up/down
for (int u = 0; u <= x; u++) {
for (int v = 0; v <= y; v++) {
if (u) up[j][u][v] = (up[j][u-1][v] + mid[j][u][v]) % mod;
if (v) down[j][u][v] = (down[j][u][v-1] + mid[j][u][v]) % mod;
}
}
}
for (int j = 0; j <= 3 * n; j++) {
for (int u = 0; u <= x; u++) {
for (int v = 0; v <= y; v++) {
f[j][u][v] = 0; //为啥这里一定要初始化
if (v) (f[j][u][v] += down[j][u][v-1]) %= mod;
if (j >= 1) (f[j][u][v] += mid[j-1][u][v]) %= mod;
if (j >= 3 && u) (f[j][u][v] += up[j-3][u-1][v]) %= mod;
}
}
}
}
for (int j = m; j <= 3 * n; j++) (ans += f[j][x][y]) %= mod;
cout << ans << endl;
}
C - 现在是,学术时间 (I)
贪心。全部放到一个人身上
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, cnt = 0;
cin >> n;
while (n --) {
int x;
cin >> x;
if (x) cnt++;
}
cout << cnt << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
}
D - 现在是,学术时间 (II)
贪心构造,画图模拟点在内部和在外部的情况进行模拟。
#include <bits/stdc++.h>
using namespace std;
double ans;
int main() {
int t;
cin >> t;
while (t --) {
int x, y, a, b;
cin >> x >> y >> a >> b;
if (a >= x && b >= y) ans = 1.0 * x * y / (1.0 * a * b);
else if (a >= x) {
double j1 = 1.0 * x * b, j2 = 1.0 * x * (y - b), s1 = 1.0 * x * y + 1.0 * b * (a - x), s2 = 1.0 * x * y + 1.0 * (y - b) * (a - x);
ans = max (j1 / s1, j2 / s2);
}
else if (b >= y) {
double j1 = 1.0 * a * y, j2 = 1.0 * (x - a) * y, s1 = 1.0 * x * y + 1.0 * a * (b - y), s2 = 1.0 * x * y + 1.0 * (x - a) * (b - y);
//cout << j1 << ' ' << j2 << ' ' << sum << endl;
ans = max (j1 / s1, j2 / s2);
}
else {
double s1 = 1.0 * a * b, s2 = 1.0 * (x - a) * b, s3 = 1.0 * a * (y - b), s4 = 1.0 * (x - a) * (y - b);
ans = max ({s1, s2, s3, s4}) / (1.0 * x * y);
}
cout << fixed << setprecision (6) << ans << endl;
}
}
E - 鸡算几何
偷jls的板子
原理:按照长度匹配,比较叉积
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using T = double;
struct Point {
T x;
T y;
Point(T x = 0, T y = 0) : x(x), y(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 &v) {
x *= v, y *= v;
return *this;
}
friend Point operator-(const Point &p) {
return Point(-p.x, -p.y);
}
friend Point operator+(Point lhs, const Point &rhs) {
return lhs += rhs;
}
friend Point operator-(Point lhs, const Point &rhs) {
return lhs -= rhs;
}
friend Point operator*(Point lhs, const T &rhs) {
return lhs *= rhs;
}
};
T dot(const Point &a, const Point &b) {
return a.x * b.x + a.y * b.y;
}
T cross(const Point &a, const Point &b) {
return a.x * b.y - a.y * b.x;
}
void solve() {
Point a[6];
for (int i = 0; i < 6; i++) cin >> a[i].x >> a[i].y;
if (cross(a[0] - a[1], a[2] - a[1]) < 0) swap(a[0], a[2]);
if (cross(a[3] - a[4], a[5] - a[4]) < 0) swap(a[3], a[5]);
double len0 = sqrt(dot(a[0] - a[1], a[0] - a[1]));
double len1 = sqrt(dot(a[3] - a[4], a[3] - a[4]));
if (abs(len0 - len1) > 1E-9) cout << "YES\n";
else cout << "NO\n";
}
int main() {
int t;
cin >> t;
while (t --) solve();
}
F - 鸡玩炸蛋人
难度在于读题。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 5, M = 4e5 + 5;
int fa[N], sz[N], n, m, a[N];
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
void Union (int x, int y) {
x = find (x), y = find (y);
fa[x] = y, sz[y] += sz[x];
}
int main () {
cin >> n >> m;
for (int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1;
while (m --) {
int a, b;
cin >> a >> b;
//cout << find (a) << ' ' << find (b) << endl;
if (find(a) != find (b)) Union (a, b);
}
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
//cout << find(i) << ' ';
if (a[i]) a[find(i)] = 1;
}
//cout << endl;
//for (int i = 1; i <= n; i++) cout << a[i] << ' '; cout << endl;
int cnt = 0, pos = 0;
for (int i = 1; i <= n; i++) {
if (fa[i] == i && a[i]) cnt ++, pos = i;
}
if (!cnt) { //无炸弹, 全部可达
ll ans = 0;
for (int i = 1; i <= n; i++) {
if (fa[i] == i) ans += 1ll * sz[i] * sz[i];
}
cout << ans;
}
else if (cnt == 1) cout << 1ll * sz[pos] * sz[pos];
else cout << 0 << endl; //两个终点, 矛盾了
}
G - 鸡格线
非常困难思维题,主要是想不到性质。
且总操作次数不大,可取20。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5;
int n, m, ans, a[N];
int f(int x) {
return round(sqrt(x) * 10);
}
signed main() {
set<int> s; //未收敛的值的下标
scanf ("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
ans += a[i];
if (f(a[i]) != a[i]) s.insert(i);
}
s.insert(n + 1); //边界
int op, l, r, k;
while (m --) {
scanf("%lld", &op);
if (op == 1) {
scanf("%lld%lld%lld", &l, &r, &k);
int pos = l;
while (1) {
int cur = (*s.lower_bound(pos));
if (cur > r) break;
int cnt = min (k, 20ll); //经验值, 最多20次
while (cnt --) {
ans -= a[cur];
a[cur] = f(a[cur]);
ans += a[cur];
}
if (f(a[cur]) == a[cur]) s.erase(cur);
pos = cur + 1;
}
}
else printf("%lld\n", ans);
}
}
H - 本题主要考察了DFS
贪心。
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
int a[4] = {0};
for (int i = 1; i < n * n; i++) {
string s;
cin >> s;
for (int j = 0; j < 4; j++) { //0,2 1,3
if (s[j] == '0') continue;
if (s[j] == '1') a[j] ++;
else a[(j + 2) % 4] --;
}
}
//for (int i = 0; i < 4; i++) cout << a[i] << ' ';cout << endl;
int ans = 0;
for (int i = 0; i < 4; i++) ans += a[i];
cout << 10 + ans << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
}
I - 本题也主要考察了DFS
J - 本题竟也主要考察了DFS
K - 本题主要考察了dp
贪心绑定 \(001\),查看剩余即可
#include <bits/stdc++.h>
using namespace std;
int n, m;
void solve() {
cin >> n >> m;
if (n - m < 2) {
cout << max(0, n - 2) << endl;
return ;
}
int cnt = (n - m) / 2 + 1;
cout << max(0, m - cnt) << endl;
}
int main() {
solve();
}
L - 本题主要考察了运气
如题。
#include<bits/stdc++.h>
using namespace std;
int main () {
cout << 32;
}
M - 本题主要考察了找规律
线性dp。f[i][j]: 放完前i个人剩下j个
#include <bits/stdc++.h>
using namespace std;
const int N = 505;
double f[N][N], ans;
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) { //还剩j个
for (int k = 0; k <= j; k++) { //这个人拿了k个
if (j) f[i][j] = max (f[i][j], f[i-1][j-k] + 1.0 * k / j);
}
ans = max (ans, f[i][j]);
}
}
cout << fixed << setprecision (7) << ans;
}
//f[i][j]: 放完前i个人剩下j个