2024牛客寒假算法基础集训营1
A:DFS搜索
字符串s 中判断是否存在子序列DFS以及子序列dfs。遍历先找D 再找F 再找S即可 dfs同理
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
void solve(){
int n; cin >> n;
string s; cin >> s;
int flag = 0, ans1 = 0, ans2 = 0;
for(int i = 0;i < n;i++){
if(s[i] == 'D' && flag == 0)
flag = 1;
else if(s[i] == 'F' && flag == 1){
flag = 2;
}
else if(s[i] == 'S' && flag == 2){
flag = 3;
}
}
if(flag == 3) ans1 = 1;
flag = 0;
for(int i = 0;i < n;i++){
if(s[i] == 'd' && flag == 0)
flag = 1;
else if(s[i] == 'f' && flag == 1){
flag = 2;
}
else if(s[i] == 's' && flag == 2){
flag = 3;
}
}
if(flag == 3) ans2 = 1;
cout << ans1 << ' ' << ans2 << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
cin >> TTT;
while (TTT--)
solve();
return 0;
}
B:关鸡
要关住鸡其实就是将两边都关住。那么便存在两种方法,一个是(2, 0) (1, -1) (1, 1)的三角形关住鸡。另外的便是边上管道形成的障碍,比如(x, 1) (x, 2) 或 (x, 1) (x + 1, 2)。
那么我们用flag表示(2, 0)是否有火;flag3 和 flag4 分别表示 (1, -1) 和 (1, 1)是否有火;flag1和flag2用于表示左端管道和右端管道的状态,0 代表一个火都没有, 1 代表有火但是没有封住路,2 代表已经封住路了。
那么接下来只需要分类讨论即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
pii q[N];
void solve(){
int n; cin >> n;
int flag = 0, flag3 = 0, flag4 = 0;
for(int i = 1;i <= n;i++){
int r, c; cin >> r >> c;
q[i] = {c, r};
if(c == 0 && r == 2)
flag = 1;
if(c == -1 && r == 1)
flag3 = 1;
if(c == 1 && r == 1)
flag4 = 1;
}
sort(q + 1, q + 1 + n);
int flag1 = 0, flag2 = 0;
for(int i = 1;i <= n;i++){
if(q[i].first < 0)
flag1 =1 ;
else if(q[i].first > 0)
flag2 = 1;
}
for(int i = 2;i <= n;i++){
if(q[i].first <= 0){
if(q[i].first == q[i - 1].first)
flag1 = 2;
else if(q[i].first == q[i - 1].first + 1 && q[i].second != q[i - 1].second)
flag1 = 2;
}
else if(q[i].first > 0){
if(q[i].first == q[i - 1].first)
flag2 = 2;
else if(q[i].first == q[i - 1].first + 1 && q[i].second != q[i - 1].second)
flag2 = 2;
}
}
int ans = 0;
if(flag1 == 0){
if(flag2 == 0){
if(flag == 1)
ans = 2;
else
ans = 3;
}
else if(flag2 == 1){
if(flag == 1 || flag4 == 1)
ans = 2;
else
ans = 3;
}
else if(flag2 == 2){
if(flag == 1)
ans = 1;
else
ans = 2;
}
}
else if(flag1 == 1){
if(flag2 == 0){
if(flag3 == 1 || flag == 1)
ans = 2;
else
ans = 3;
}
else if(flag2 == 1){
if(flag3 == 1 && flag4 == 1)
ans = 1;
else
ans = 2;
}
else if(flag2 == 2)
ans = 1;
}
else{
if(flag2 == 0){
if(flag == 1)
ans = 1;
else
ans = 2;
}
else if(flag2 == 1){
ans = 1;
}
else if(flag2 == 2)
ans = 0;
}
//cout << flag << ' ' << flag1 << ' ' << flag2 << ' '<< flag3 << ' ' << flag4 << endl;
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
cin >> TTT;
while (TTT--)
solve();
return 0;
}
C:按闹分配
要总不满意度最小,那么显然是从小到大安排最好。而假设鸡插队后面有K个人,那么其实就是这K个人多等了 \(t_c\)时间,总忍耐度就多了\(K * t_c\) 时间,那么对于容忍限度M找出最大的K 然后插入计算前面需要等的时间即可。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int a[N], sum[N];
void solve(){
int n, m, t;
cin >> n >> m >> t;
for(int i = 1;i <= n;i++)
cin >> a[i];
sort(a + 1,a + 1 + n);
for(int i = 1;i <= n;i++)
sum[i] = sum[i - 1] + a[i];
while(m--){
ll ren; cin >> ren;
ll cnt = min(n, ren / t);
ll ans = sum[n - cnt] + t;
cout << ans << endl;
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
//cin >> TTT;
while (TTT--)
solve();
return 0;
}
D:数组成鸡
M的大小为\([-1e9,1e9]\) ,那么对于不同的数字相乘,其实最多接受不到20个不同的数字。那用map记录不同的数字以及出现次数,若是数字种类大于20直接排除;接下来考虑20种及以内的数字种类,
由于n 大于等于2,那么对于所有乘积结果,一定有一个数在\([-\sqrt{1e9}, \sqrt{1e9}]\)之间,所以对于每种数,去遍历区间\([-1e5,1e5]\) 即可得到所有的能得到的乘积。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int a[N];
map<int,int>mp;
void solve(){
int n, q; cin >> n >> q;
for(int i = 1;i <= n;i++)
cin >> a[i];
sort(a + 1,a + 1 + n);
for(int i = 1;i <= n;i++)
mp[a[i]]++;
set<int> st;
st.insert(0);
if(mp.size() <= 20){
for(auto it : mp){
for(int i = -1e5;i <= 1e5;i++){
int cur = it.first - i;
int ans = 1;
for(auto j : mp){
int u = j.first - cur;
if(u == 1) continue;
if(u == -1){
if(j.second % 2)
ans *= -1;
continue;
}
for(int k = 0;k < j.second;k++){
ans = ans * u;
if(abs(ans) > 1e9)
break;
}
if(abs(ans) > 1e9)
break;
}
if(abs(ans) < 1e9)
st.insert(ans);
}
}
}
while(q--){
int m; cin >> m;
if(st.count(m))
cout << "Yes\n";
else
cout << "No\n";
}
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
//cin >> TTT;
while (TTT--)
solve();
return 0;
}
E:本题又主要考察了贪心
观察n m都极小,考虑暴力,直接爆搜所有情况即可,对于每一场比赛有三种结局。复杂度为\(O(m ^ 3 * T)\)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
pii q[15];
int a[15];
int n, m, ans = INF;
void dfs(int now){
if(now > m){
int cnt = 1;
for(int i = 2;i <= n;i++)
if(a[i] > a[1]) cnt++;
ans = min(ans, cnt);
return;
}
int u = q[now].first, v = q[now].second;
a[u] += 3;
dfs(now + 1);
a[u] -= 3;
a[u] += 1; a[v] += 1;
dfs(now + 1);
a[u] -= 1; a[v] -= 1;
a[v] += 3;
dfs(now + 1);
a[v] -= 3;
}
void solve(){
cin >> n >> m;
for(int i = 1;i <= n;i++)
cin >> a[i];
for(int i = 1;i <= m;i++){
int u, v; cin >> u >> v;
q[i] = {u, v};
}
ans = INF;
dfs(1);
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
cin >> TTT;
while (TTT--)
solve();
return 0;
}
F:鸡数题!
\(a_1∣a_2∣...∣a_{m−1}∣a_m=2^n−1\) 这个就说明m个数能组成n个二进制的全部的位
对于任意的 \(i≠j\) ,满足$a_i $ & $ a_j=0$ 这个代表没有m个数之间没有重复的二进制位
所以就相当于 n 个二进制数分到 m 个数里
对于任意的整数\(1≤i≤m−1\),\(a_i<a_{i+1}\) 所以说明 这n个数之间是不一样的
那就相当于 n 个不同的球 放到 m 个不同的箱子里 有几种方法,那就是第二类斯特林数(具体内容可查询博客)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int fac[N], infac[N];
ll C(ll n,ll m){//n个数选m个数
return fac[n] * infac[n - m] % mod * infac[m] % mod;
}
long long binpow(long long a, long long b, long long m) {//a为底数 b为指数 m为模数
a %= m;
long long res = 1;
while (b > 0) {
if (b & 1) res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
void solve(){
int n, m; cin >> n >> m;
ll ans = 1;
for(int i = 1;i <= m;i++)
ans = (ans * i) % mod;
ans = binpow(ans, mod - 2, mod);
ll sum = 0;
for(int k = 0;k <= m;k++){
if(k % 2 == 0)
sum = (sum + (C(m, k) % mod * binpow(m - k, n, mod) % mod)) % mod;
else
sum = (sum - (C(m, k) % mod * binpow(m - k, n, mod) % mod)) % mod;
}
ans = (ans * sum) % mod;
cout << (ans + mod ) % mod << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
//cin >> TTT;
fac[0] = 1;
infac[0] = 1;
for (int i = 1; i <= 1e6;i++){
fac[i] = i * fac[i - 1] % mod;
infac[i] = binpow(fac[i], mod - 2, mod);
}
while (TTT--)
solve();
return 0;
}
G:why买外卖
则所有满足\(x >= a_i\)的满减优惠都可以一起同时被使用,那么我们可以先对满减券按\(a_i\)从小到大排序,那么对于所有\(i >= 2\) \(a_i\)可以使用的时候,前面的也都可以使用,所以此时就贪心遍历找最大能接受的额度即可。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
pii q[N];
int n, m;
void solve(){
cin >> n >> m;
ll ans = m;
for(int i = 1;i <= n;i++){
int u, v; cin >> u >> v;
q[i] = {u, v};
}
sort(q + 1,q + 1 + n);
ll sum = 0;
for(int i = 1;i <= n;i++){
sum += q[i].second;
if(q[i].first - sum <= m)
ans = max(ans, m + sum);
}
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
cin >> TTT;
while (TTT--)
solve();
return 0;
}
H:01背包,但是bit
设我们最后选择的重量和为 c。对于m 枚举每一位是1的bit。假设当前枚举到下标为 i ,只需要将该位设为0 ,那么对于\([30, i + 1]\) 的位置,c 必须是 m 的子集这样才能保证c 不大于m;对于\([i - 1, 0]\) 就可以任意选择,为了贪心所以全部都选1。再加上等于c == m的情况,那么 c 的所有情况便有了。对于所有情况再去比较谁获得的价值和最大即可。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int v[N], w[N], n;
ll check(ll m){
ll res = 0;
for(int i = 1;i <= n;i++){
if((w[i] & m) == w[i])
res += v[i];
}
return res;
}
void solve(){
int m; cin >> n >> m;
for(int i = 1;i <= n;i++)
cin >> v[i] >> w[i];
ll ans = check(m);
for(int i = 29;i >= 0;i--)
if((1 << i) & m)
ans = max(ans, check((m ^ (1 << i)) | ((1 << i) - 1)));
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
cin >> TTT;
while (TTT--)
solve();
return 0;
}
I:It's bertrand paradox. Again!
这题比较玄学,就是看概率。第一种选中边缘的位置是与其他位置概率相同;但是第二种要则几乎是第一种的百分之一,因为要同时要求半径很小。 所以接近平均概率的就是第一种,否则就是第二种。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
void solve(){
int n; cin >> n;
int cnt = 0;
for(int i = 1;i <= n;i++){
int x, y, r;
cin >> x >> y >> r;
if(abs(x) >= 98 && abs(y) >= 98)
cnt++;
}
if(cnt >= 10)
cout << "bit-noob\n";
else
cout << "buaa-noob\n";
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
//cin >> TTT;
while (TTT--)
solve();
return 0;
}
K:牛镇公务员考试
题面很绕,但是理解之后就会发现,所有点都只有一个出度。然后只需要找到所有的环,在这环上任意找一个点作为起点,暴力5种情况并且走一圈看看是否存在矛盾,若是无矛盾则这环内的方案++,最后答案便是所有环的方案相乘。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
struct node{
int to;
string s;
}a[N];
int ind[N], tod[N];
vector<int>G[N];
int check(int indx){
int ans = 0;
for(int i = 0;i < 5;i++){
int pos = indx, choice = i;
while(1){
choice = a[pos].s[choice] - 'A';
pos = a[pos].to;
if(pos == indx){
if(choice == i) ans++;
break;
}
}
}
return ans;
}
void solve(){
int n; cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i].to >> a[i].s;
G[i].push_back(a[i].to);
tod[i]++; ind[a[i].to]++;
}
ll ans = 1;
vector<int> vis(n + 10, -1);
for(int i = 1;i <= n;i++){
int u = i;
while(vis[u] == -1){
vis[u] = i;
u = a[u].to;
}
if(vis[u] == i){
int res = check(u);
ans = (ans * res) % 998244353;
}
}
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
//cin >> TTT;
while (TTT--)
solve();
return 0;
}
L:要有光
诈骗题,仔细阅读题面发现要求的是地面的阴影而不是墙上的。那就是梯形的面积
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int a[N], sum[N];
void solve(){
int c, d, h, w; cin >> c >> d >> h >> w;
cout << 3 * w * c << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
cin >> TTT;
while (TTT--)
solve();
return 0;
}
M:牛客老粉才知道的秘密
首先看 n 是否为 6 的倍数。
-
n是6的倍数,那么从左开始和从右开始其实会重合,所以答案就是n / 6;
-
n不是6的倍数,那么从左开始和从右开始就是错开的,答案便是n / 6 * 2;
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
void solve(){
int n; cin >> n;
int ans = 0;
if(n % 6 == 0){
ans = n / 6;
}
else
ans = n / 6 * 2;
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int TTT = 1;
cin >> TTT;
while (TTT--)
solve();
return 0;
}

浙公网安备 33010602011771号