2024牛客寒假算法基础集训营1
A-DFS搜索
暴力判就行
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
void solve() {
int n;
string s;
cin >> n >> s;
int f = 0;
for (int i = 0; i < n; i++) {
if (s[i] != 'D') continue;
for (int j = i + 1; j < n; j++) {
if (s[j] != 'F') continue;
for (int k = j + 1; k < n; k++) {
if (s[k] != 'S') continue;
f = 1;
break;
}
break;
}
break;
}
cout << f << " ";
f = 0;
for (int i = 0; i < n; i++) {
if (s[i] != 'd') continue;
for (int j = i + 1; j < n; j++) {
if (s[j] != 'f') continue;
for (int k = j + 1; k < n; k++) {
if (s[k] != 's') continue;
f = 1;
break;
}
break;
}
break;
}
cout << f << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
for (cin >> T; T; T--)
solve();
return 0;
}
B-关鸡
只要分别堵住左右的边界就行。但是有一种特殊的情况就是左右的边界共用一个\((0,2)\)点的情况。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
void solve() {
int n;
vector<set<int>> l(2), r(2);
cin >> n;
int a = 2, b = 2;
for (int x, y; n; n--) {
cin >> x >> y, --x;
if (y <= 0) l[x].insert(y), a = 1;
if (y >= 0) r[x].insert(y), b = 1;
}
for (int x = 0; x < 2 and a == 1; x++) {
for (const auto &y: l[x]) {
if (l[x ^ 1].count(y)) {
a = 0;
break;
} else if (l[x ^ 1].count(y - 1)) {
a = 0;
break;
} else if (l[x ^ 1].count(y + 1)) {
a = 0;
break;
}
}
}
for (int x = 0; x < 2 and b == 1; x++) {
for (const auto &y: r[x]) {
if (r[x ^ 1].count(y)) {
b = 0;
break;
} else if (r[x ^ 1].count(y - 1)) {
b = 0;
break;
} else if (r[x ^ 1].count(y + 1)) {
b = 0;
break;
}
}
}
int ans = 3;
if (l[0].count(-1)) ans--;
if (l[1].count(0)) ans--;
if (r[0].count(1)) ans--;
cout << min(a + b, ans) << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
for (cin >> T; T; T--)
solve();
return 0;
}
C-按闹分配
首先可以想到最优的分配方法就是用时少的人先办理。
可以推出的是,插队插在\(x\)个人前面则\(S_c-S_{min}=xt_c\le M\),所以算\(x\)在求个和即可。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, q, tc;
cin >> n >> q >> tc;
vi t(n);
for (auto &i: t) cin >> i;
t.push_back(0);
sort(t.begin(), t.end());
for (int i = 1; i <= n; i++)
t[i] += t[i - 1];
for (int m, x; q; q--) {
cin >> m;
x = m / tc;
x = max(0ll, n - x);
cout << t[x] + tc<< "\n";
}
return 0;
}
D-数组成鸡
因为询问的范围并不算很大,并且最终的结果要乘积。所以其实整个数组可以改变的部分并不多。可以把每一个数变成0,然后再这个数的周围暴力枚举即可,只要超出边界就可以结束。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod, y /= 2;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, q;
cin >> n >> q;
vi a(n);
for (auto &i: a) cin >> i;
sort(a.begin(), a.end());
set<int> cnt;
auto check = [a, &cnt](int x) {
i128 ans = 1;
for (const auto &i: a) {
ans = ans * (x + i);
if (ans > 1e9 or ans < -1e9)break;
}
if (ans > 1e9 or ans < -1e9) return false;
cnt.insert(ans);
return true;
};
for (int i = 0; i < n; i++) {
if (i and a[i] == a[i - 1]) continue;
for (int j = -a[i] - 1; check(j); j--);
for (int j = -a[i] + 1; check(j); j++);
}
cnt.insert(0);
for (int x; q; q--) {
cin >> x;
if (cnt.count(x)) cout << "Yes\n";
else cout << "No\n";
}
return 0;
}
E-本题又主要考察了贪心
因为\(m\)只有\(10\)所以\(3^{10}\)级别的暴力枚举是可以接受的
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int n, m, res;
vector<int> a, x, y;
void dfs(int i) {
if (i == m) {
int ans = 1;
for (int i = 1; i < n; i++)
ans += (a[i] > a[0]);
res = min(res, ans);
return;
}
a[x[i]] += 3;
dfs(i + 1);
a[x[i]] -= 3;
a[y[i]] += 3;
dfs(i + 1);
a[y[i]] -= 3;
a[x[i]]++, a[y[i]]++;
dfs(i + 1);
a[x[i]]--, a[y[i]]--;
return;
}
void solve() {
cin >> n >> m, res = inf;
a.resize(n), x.clear(), y.clear();
for (auto &i: a) cin >> i;
for (int u, v; m; m--) {
cin >> u >> v, u--, v--;
if (u == 0 or v == 0) a[0] += 3;
else x.push_back(u), y.push_back(v);
}
m = x.size();
dfs(0);
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
for (cin >> T; T; T--)
solve();
return 0;
}
F-鸡数题!
根据题目,其实可以转化为第二类斯特林数。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod, y /= 2;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
if (n < m) {
cout << "0\n";
return 0;
}
vi fact(n + 1);
fact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % mod;
int res = 0;
for (int i = 0, t; i <= m; i++) {
t = power(i, n) * inv(fact[i]) % mod * inv(fact[m - i]) % mod;
if ((m - i) % 2 == 0) res = (res + t) % mod;
else res = (res - t + mod) % mod;
}
cout << res << "\n";
return 0;
}
why买外卖
把所有的优惠卷按照\(a_i\)排序,然后枚举在前\(i\)个卷全部使用的情况下,是否可以满足原价大于等\(a_i\)
也就是
是否成立
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
void solve() {
int n, m;
cin >> n >> m;
vector<pii> a(n);
for (auto &[x, y]: a) cin >> x >> y;
sort(a.begin(), a.end());
int res = m , cnt = m;
for (const auto &[x, y]: a) {
cnt += y;
if( cnt >= x) res = max( res , cnt);
}
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
for (cin >> T; T; T--)
solve();
return 0;
}
H-01背包,但是bit
如果要使得当前背包中的物品重量不超过\(x\)只需要选择所有满足\(x|w_i = x\)的物品即可。
在这种情况下,也就是要枚举所有的\(x\le m\),然后计算出结果即可。
但是显然在满足小于等于\(m\)的情况下,二进制下1的个数越多越好。所以我们可以枚举\(m\)二进制下所有为\(1\)的位,然后把这一位变位\(0\),把低位全部变为\(1\)来做\(x\)。这样符合条件的\(x\)不超过30个。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod, y /= 2;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
void solve() {
int n, m;
cin >> n >> m;
vi v(n), w(n);
int res = 0;
for (int i = 0; i < n; i++) cin >> v[i] >> w[i];
auto calc = [v, w, n](int x) {
int ans = 0;
for (int i = 0; i < n; i++)
if ((x | w[i]) == x) ans += v[i];
return ans;
};
res = max(res, calc(m));
for (int i = 0; i < 31; i++)
if (m & (1 << i)) res = max(res, calc(m ^ (1 << i) | ((1 << i) - 1)));
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int TC;
for (cin >> TC; TC; TC--)
solve();
return 0;
}
I-It's bertrand paradox. Again!
两种方案,bit的方法相当于先随机出\(x,y\)在随机出\(r\),buaa的方法相当于每次都直接随机出\((x,y,r)\),自然的bit的分布更加均匀,而buaa的方法则相对集中在中心部位。
所以写出这两种方法,分别随机100次,然后看圆心在中间的情况(\(-50\le x ,y\le 50\))的概率,bit的概率大约是\(0.25\),buaa的概率大约是\(0.5\)
根据这个分辨一下就好。
test()
是我的暴力代码
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod, y /= 2;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
void test() {
mt19937 mt{random_device()()};
uniform_int_distribution rdx(-99, 99);
uniform_int_distribution rdr(1, 100);
int n = 1e5;
auto buaa = [&]() {
vector<array<int, 3>> a;
for (int t = n, x, y, r; t; t--) {
while (true) {
x = rdx(mt), y = rdx(mt), r = rdr(mt);
if (r < 0 or abs(x + r) > 100 or abs(x - r) > 100 or abs(y + r) > 100 or abs(y - r) > 100) continue;
break;
}
a.push_back({x, y, r});
}
return a;
};
auto bit = [&]() {
vector<array<int, 3>> a;
for (int t = n, x, y, r; t; t--) {
x = rdx(mt), y = rdx(mt);
while (true) {
r = rdr(mt);
if (r < 0 or abs(x + r) > 100 or abs(x - r) > 100 or abs(y + r) > 100 or abs(y - r) > 100) continue;
break;
}
a.push_back({x, y, r});
}
return a;
};
int t = 100;
ldb x = 0, y = 0;
for (int i = 1; i <= t; i++) {
ldb xx = 0, yy = 0;
for (auto [x, y, r]: bit())
if (abs(x) <= 50 and abs(y) <= 50) xx += 1;
for (auto [x, y, r]: buaa())
if (abs(x) <= 50 and abs(y) <= 50) yy += 1;
x += xx / n, y += yy / n;
cerr << "bit = " << xx / n << " buaa = " << yy / n << " \n";
}
x /= t, y /= t;
cout << fixed << setprecision(6);
cout << "bit = " << x << " buaa = " << y << " \n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
// test();
int n;
cin >> n;
ldb cnt = 0;
for( int i = 1 , x , y , r ; i <= n ; i ++ ){
cin >> x >> y >> r;
if( abs(x) <= 50 and abs(y) <= 50 ) cnt += 1;
}
cnt /= n;
if( abs( cnt - 0.25) < abs(cnt-0.5)) cout << "bit-noob\n";
else cout << "buaa-noob\n";
return 0;
}
J-又鸟之亦心
\(i\)时刻,一定有一个人位于\(a_i\) ,用set
维护另一个人可能位于的位置,保证整个过程种set
必须保证非空。这样我们就可以二分答案。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod, y /= 2;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, x, y;
cin >> n >> x >> y;
vi a(n);
for (auto &i: a) cin >> i;
int l = 0, r = 1e9, res = -1;
auto check = [=](int d) -> bool {
int lst = y;
set<int> s;
if (abs(x - y) <= d) s.insert(x);
else return false;
for (int i: a) {
if ( abs(i - lst) <= d) s.insert(lst);
while (not s.empty() and *s.begin() < i - d) s.erase(*s.begin());
while (not s.empty() and *s.rbegin() > i + d) s.erase(*s.rbegin());
lst = i;
if (s.empty()) break;
}
return not s.empty();
};
for (int mid; l <= r;) {
mid = (l + r) / 2;
if (check(mid)) res = mid, r = mid - 1;
else l = mid + 1;
}
cout << res << "\n";
return 0;
}
K-牛镇公务员考试
\(i\)可以影响\(a_i\),所以图一定是一个内向基环树森林。
基环树上的链无法影响答案,因为可以从环上的结点反推出答案。对于任意一个环,随便选择一个点,然后枚举所有的答案,最后判断一下即可。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod, y /= 2;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vi a(n);
vector<string> s(n);
vi vis(n), inDeg(n);
for (int i = 0; i < n; i++) cin >> a[i] >> s[i], a[i]--, inDeg[a[i]]++;
queue<int> q;
for (int i = 0; i < n; i++)
if (inDeg[i] == 0) q.push(i);
for (int x, y; not q.empty();) {
x = q.front(), q.pop();
vis[x] = 1, y = a[x];
if (--inDeg[y] == 0) q.push(y);
}
int res = 1;
for (int i = 0, ans; i < n; i++) {
if (vis[i]) continue;
ans= 0, vis[i] = 1;
for (int u = 0, v, t; u < 5; u++) {
t = i, v = u;
do {
v = s[t][v] - 'A';
t = a[t];
vis[t] = 1;
} while (t != i);
ans += v == u;
}
res = res * ans % mod;
}
cout << res << "\n";
return 0;
}
L-要有光
根据高中的地理知识就可以猜出,当太阳的高度为0时,影子面积最大
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
int power(int x, int y) {
int ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
x = x * x % mod, y /= 2;
}
return ans;
}
int inv(int x) {
return power(x, mod - 2);
}
void solve() {
ldb d, c, h, w, x, y, res;
cin >> c >> d >> h >> w;
x = 2 * c, y = 2 * w;
res = x * y - c * w;
cout << fixed << setprecision(8) << res << "\n";
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
for (cin >> T; T; T--)
solve();
return 0;
}
M-牛客老粉才知道的秘密
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int long long
using vi = vector<int>;
using pii = pair<int, int>;
using vii = vector<pii>;
const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;
void solve() {
int n, res = 0;
cin >> n;
res = n / 6;
if (n % 6 != 0) res = res * 2;
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
for (cin >> T; T; T--)
solve();
return 0;
}