2024牛客寒假算法基础集训营1
A
解题思路:
按照\(dfs\)出现顺序暴力判断即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n;
cin >> n;
string s;
cin >> s;
vector<bool> vis(40);
for (auto c : s)
{
if (c == 'D')
{
vis['D' - 'A'] = true;
}
else if (c == 'F' && vis['D' - 'A'] == true)
{
vis[c - 'A'] = true;
}
else if (c == 'S' && vis['F' - 'A'] == true)
{
vis[c - 'A'] = true;
}
}
if (vis['D' - 'A'] && vis['F' - 'A'] && vis['S' - 'A'])
{
cout << 1 << ' ';
}
else
{
cout << 0 << ' ';
}
for (int i = 0; i < 30; i++)
{
vis[i] = false;
}
for (auto c : s)
{
if (c == 'd')
{
vis['d' - 'a'] = true;
}
else if (c == 'f' && vis['d' - 'a'] == true)
{
vis[c - 'a'] = true;
}
else if (c == 's' && vis['f' - 'a'] == true)
{
vis[c - 'a'] = true;
}
}
if (vis['d' - 'a'] && vis['f' - 'a'] && vis['s' - 'a'])
{
cout << 1 << ' ';
}
else
{
cout << 0 << ' ';
}
cout << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
B
解题思路:
如果\((2,0),(1,-1),(1,1)\)这三处有火,那么三把火就够了。
其余封锁起码四把火,左右各两把。
暴力记录枚举情况添火判断即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n;
cin >> n;
set<pii> s;
bool l = false;
bool tr = false;
ll res = 3;
ll minx = 1e18;
ll maxx = -1e18;
for (int i = 1; i <= n; i++)
{
ll r, c;
cin >> r >> c;
minx = min(minx, c);
maxx = max(maxx, c);
if (r == 1)
{
if (s.find({r + 1, c}) != s.end())
{
if (c < 0)
{
l = true;
}
else if (c > 0)
{
tr = true;
}
}
if (s.find({r + 1, c - 1}) != s.end())
{
if (c - 1 >= 0)
{
tr = true;
}
else
{
l = true;
}
}
if (s.find({r + 1, c + 1}) != s.end())
{
if (c + 1 > 0)
{
tr = true;
}
else
{
l = true;
}
}
}
else
{
if (s.find({r - 1, c}) != s.end())
{
if (c < 0)
{
l = true;
}
else if (c > 0)
{
tr = true;
}
}
if (s.find({r - 1, c - 1}) != s.end())
{
if (c - 1 >= 0)
{
tr = true;
}
else
{
l = true;
}
}
if (s.find({r - 1, c + 1}) != s.end())
{
if (c + 1 > 0)
{
tr = true;
}
else
{
l = true;
}
}
}
s.insert({r, c});
}
if (s.find({2, 0}) != s.end())
{
res--;
}
if (s.find({1, -1}) != s.end())
{
res--;
}
if (s.find({1, 1}) != s.end())
{
res--;
}
ll ans = 0;
if (!l)
{
if (s.size())
{
auto x = minx;
if (x <= 0)
{
ans += 1;
}
else
{
ans += 2;
}
}
else
{
ans += 2;
}
}
// cout << ans << endl;
if (!tr)
{
if (s.size())
{
auto x = maxx;
if (x >= 0)
{
ans += 1;
}
else
{
ans += 2;
}
}
else
{
ans += 2;
}
}
ans = min(ans, res);
cout << ans << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
C
解题思路:
先求出总不满意度\(S\)
\(S_c - S_{min} \leq M\)
\(S_{min} \geq S_c - M\)
尝试插入发现,越往后插队,\(S_{min}\)越大,具有单调性,二分即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
ll n, q, t;
cin >> n >> q >> t;
vector<ll> a(n + 1), pre(n + 10);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a.begin() + 1, a.end());
ll sum = 0;
for (int i = 1; i <= n; i++)
{
sum += (n - i + 1) * a[i];
pre[i] = a[i] + pre[i - 1];
}
while (q--)
{
ll m;
cin >> m;
auto check = [&](int mid)
{
ll cur = t * (n - mid + 1);
return cur <= m;
};
int l = 0;
int r = n + 1;
while (l + 1 < r)
{
int mid = l + r >> 1;
if (check(mid))
{
r = mid;
}
else
{
l = mid;
}
}
cout << pre[r - 1] + t << endl;
}
}
int main()
{
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
return 0;
}
D
解题思路:
\((-10^9 \leq M \leq 10^9)\)。
累乘发现,\(9! \times 9! > 10^9\)。也就是说出现\(19\)个不同的数字后怎么改绝对值都会大于\(10^9\)。当然,\(0\)除外。
实际上,我们将这少数不同的数分别变成从\(0\)开始不断累加或者累减,哪怕\(n = 2\),最多\(\sqrt{10^9}\)次之后,序列累乘值一定突破询问上限。
所以,我们可以\(O(n\sqrt{10^9})\)内求出所有可得到的数字。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const int N = 1e5 + 10;
const int inf = 1e9;
int n, q;
ll a[N];
set<ll> s, ans;
bool check(ll x)
{
ll res = 1;
for (int i = 1; i <= n; i++)
{
res *= a[i] + x;
if (abs(res) > inf)
{
return false;
}
}
ans.insert(res);
return true;
}
void solve()
{
cin >> n >> q;
ans.insert(0);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
s.insert(a[i]);
}
if (s.size() < 20)
{
// 将i从1/-1开始递增和递减,最多根号n次
for (auto i : s)
{
ll cur = -i + 1;
while (check(cur))
{
cur++;
}
cur = -i - 1;
while (check(cur))
{
cur--;
}
}
}
while (q--)
{
int x;
scanf("%d", &x);
if (ans.count(x))
{
puts("Yes");
}
else
{
puts("No");
}
}
}
int main()
{
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
return 0;
}
E
解题思路:
数据范围很小,对于每场比赛对决枚举所有胜负可能。
\(O(m \times 3^m)\)
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n, m;
cin >> n >> m;
vector<int> a(n + 1);
vector<pii> q;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
if (u == 1 || v == 1)
{
a[1] += 3;
continue;
}
q.push_back({u, v});
}
m = q.size();
ll ans = 1e18;
ll sum = pow(3, m);
for (int i = 0; i < sum; i++)
{
ll t = i;
vector<int> p;
vector<int> b = a;
for (int i = 1; i <= m; i++)
{
p.push_back(t % 3);
t /= 3;
}
int cur = 0;
for (auto x : p)
{
int u = q[cur].fi;
int v = q[cur].se;
cur++;
if (x == 0)
{
b[u]++;
b[v]++;
}
else if (x == 1)
{
b[u] += 3;
}
else
{
b[v] += 3;
}
}
ll cnt = 0;
for (int i = 1; i <= n; i++)
{
if (b[i] > b[1])
{
cnt++;
}
}
ans = min(ans, cnt + 1);
}
cout << ans << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
F
解题思路:
本题考察第二类斯特林数的通项公式。
\[S(n,m) = {n \brace m} = \frac{1}{m!} \times \sum\limits_{k = 0}^{m}((-1)^{k}\times\binom{m}{k} \times (m - k)^{n}) \]代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const int N = 1e5;
const int mod = 1e9 + 7;
ll f[N + 1];
ll invf[N + 1];
ll qmi(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
{
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
ll C(ll a, ll b)
{
if (b == 0 || a == b)
{
return 1;
}
return (f[a] * invf[b] % mod) * invf[a - b] % mod;
}
void solve()
{
int n, m;
cin >> n >> m;
if (n < m)
{
cout << 0 << endl;
return;
}
f[0] = 1;
for (int i = 1; i <= N; i++)
{
f[i] = f[i - 1] * i % mod;
}
invf[N] = qmi(f[N], mod - 2) % mod;
for (int i = N - 1; i; i--)
{
invf[i] = invf[i + 1] * (i + 1) % mod;
}
ll ans = 0;
for (int i = 0; i <= m; i++)
{
ll sign = (i & 1) ? -1 : 1;
ans = (ans + (sign * C(m, i) * qmi(m - i, n) % mod)) % mod;
ans = (ans + mod) % mod;
}
ans = ans * invf[m] % mod;
cout << ans << endl;
}
int main()
{
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
return 0;
}
G
解题思路:
显然,满减券的时候小的都叠加起来,手机里的钱加满减能得到上限。记得将手机里没用完的钱加上。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
ll n, m;
cin >> n >> m;
vector<pii> v(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> v[i].fi >> v[i].se;
}
sort(v.begin() + 1, v.end());
ll pre = 0;
ll ans = m;
for (int i = 1; i <= n; i++)
{
pre += v[i].se;
if (v[i].fi - pre <= m)
{
ans = max(ans, v[i].fi + m - (v[i].fi - pre));
}
}
cout << ans << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
H
解题思路:
我们总重量枚举二进制中所有的\(1\)位,将该为改为\(0\),同时低位全部变为\(1\)进行讨论,记得最后总重量本身讨论一下。
举例:\(m = 1101010100\)
将其划分为几段
\([0,0111111111]\)
$ [1000000000,1011111111]$
\([1100000000,1100111111]\)
\([1101000000,1101001111]\)
\([1101010000,1101010011]\)
\([1101010100]\)
\(m\)中有\(5\)个一,所以前五段对应的就是这些一的变化讨论。最后一个就是总重量本身讨论。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n, m;
cin >> n >> m;
vector<int> v(n + 1), w(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> v[i] >> w[i];
}
ll ans = 0;
ll cur = 0;
ll res = 0;
for (int i = 30; i >= 0; i--)
{
if (m >> i & 1)
{
res = 0;
ll t = cur + (1 << i) - 1;
for (int j = 1; j <= n; j++)
{
if ((w[j] | t) == t)
{
res += v[j];
}
}
ans = max(ans, res);
cur += 1 << i;
}
}
res = 0;
for (int i = 1; i <= n; i++)
{
if ((w[i] | cur) == cur)
{
res += v[i];
}
}
ans = max(ans, res);
cout << ans << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
I
解题思路:
按照题目所给构造方法进行构造,然后统计二者某一数值的平均或者其他统计特征,找到明显区别,即可判断。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
ll n;
cin >> n;
n = 1e5;
double s = 0;
for (int i = 1; i <= n; i++)
{
ll a, b, c;
cin >> a >> b >> c;
s += c;
}
s /= n;
if (s >= 20)
{
puts("buaa-noob");
}
else
{
puts("bit-noob");
}
}
int main()
{
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
return 0;
}
J
解题思路:
二分。
每次二分判断尝试构造任务过程,使得二者过程期间最大距离小于\(mid\),存在则说明\(mid\)在逼近。
在构造过程当中,假设我们当前遍历到了\(i\)处,此时有一个人一定在\(a_i\)处。查看另一位可出现的位置集合。\((集合初始就是只有(x,y),其余后续随着遍历插入)\)
如果存在\(abs(a_i - a_j) > mid\),此时我们就应当将\(a_j\)移出集合,因为\(a_j\)参与构建这一步会使得过程最大值过大。如果将集合内所有\(a_j\)删除后,集合为空,则说明这一步二者间的距离不可能小于等于\(mid\)。
对于\(i - 1\)删去的\(a_j\),即使\(abs(a_{i + 1} - a_j) \leq mid\),\(a_j\)理论上已不可能和\(a_{i + 1}\)同时出现促成一对。
因为,如果\(a_{i + 1}\)的对象能选\(a_j\),说明这一步一定是有一个人从\(a_i\)走到\(a_{i + 1}\),因为在\(i\)时刻一定有一个人在\(a_i\)。
我们按照时间倒推,如果\(i + 1\)时刻两人位置是\((a_{i +1},a_j)\),那么\(i\)时刻两人位置就是\((a_i,a_j)\),即非法状态。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n, x, y;
cin >> n >> x >> y;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
ll l = -1;
ll r = 1e9 + 1;
auto check = [&](ll mid)
{
set<int> s;
if (abs(x - y) > mid)
{
return false;
}
s.insert(x);
s.insert(y);
for (int i = 1; i <= n; i++)
{
while (s.size() && abs((*s.begin()) - a[i]) > mid)
{
s.erase(s.begin());
}
while (s.size() && abs((*s.rbegin()) - a[i]) > mid)
{
s.erase(*s.rbegin());
}
if (s.empty())
{
return false;
}
s.insert(a[i]);
}
return true;
};
while (l + 1 < r)
{
ll mid = l + r >> 1;
if (check(mid))
{
r = mid;
}
else
{
l = mid;
}
}
cout << r << endl;
}
int main()
{
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
return 0;
}
K
解题思路:
\(n\)个点,\(n\)条边。基环树。
找到每个环。对该环搜索。每个结点开始有\(5\)种选择,依次顺着选下去,如果能沿着环走回来,说明得到一份使得该环上所有题都正确的答案集合。
将所有环的正确答案集合数量累乘,即答案。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const int mod = 998244353;
void solve()
{
int n;
cin >> n;
vector<int> a(n + 1);
vector<string> s(n + 1);
vector<int> indeg(n + 1);
vector<bool> vis(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i] >> s[i];
indeg[a[i]]++;
}
queue<int> q;
for (int i = 1; i <= n; i++)
{
if (!indeg[i])
{
q.push(i);
}
}
while (q.size())
{
auto t = q.front();
q.pop();
indeg[a[t]]--;
vis[t] = true;
if (!indeg[a[t]])
{
q.push(a[t]);
}
}
ll ans = 1;
for (int i = 1; i <= n; i++)
{
if (vis[i])
{
continue;
}
int u = i;
int v = u;
while (true)
{
vis[v] = true;
v = a[v];
if (v == u)
{
break;
}
}
int cnt = 0;
for (int j = 0; j < 5; j++)
{
v = u;
int idx = j;
while (true)
{
idx = s[v][idx] - 'A';
v = a[v];
if (v == u)
{
break;
}
}
if (s[u][idx] == s[u][j])
{
cnt++;
}
}
ans = ans * cnt % mod;
}
cout << ans << endl;
}
int main()
{
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
return 0;
}
L
解题思路:
简单画个图,答案为\(3wc\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
ll c,d,h,w;
cin >> c >> d >> h >> w;
cout << 3 * w * c << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
M
解题思路:
分别从左和右开始排放。
如果总长度是\(6\)的倍数只需从任意一端排放依次,否则就是两次排放可能之和。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n;
cin >> n;
if (n % 6 == 0)
{
cout << n / 6 << endl;
}
else
{
cout << (n / 6) * 2 << endl;
}
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
标签:int,ll,long,2024,牛客,pair,using,集训营,define
From: https://www.cnblogs.com/value0/p/18014193