在2023年牛客寒假算法基础集训营开始前,刚好没什么事,就来补一下2022年的寒假集训题目。
L题:
牛牛学走路
做法:正常模拟一下就行。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
void solve()
{
int n;
string s;
cin >> n >> s;
int a = 0, b = 0;
double ans = 1.0;
for (auto c : s)
{
if (c == 'L')
{
a--;
ans = max(ans, sqrt(a * a + b * b));
}
else if (c == 'R')
{
a++;
ans = max(ans, sqrt(a * a + b * b));
}
else if (c == 'U')
{
b++;
ans = max(ans, sqrt(a * a + b * b));
}
else
{
b--;
ans = max(ans, sqrt(a * a + b * b));
}
}
printf("%.12f\n", ans);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
E题:
炸鸡块君的高中回忆
做法:不能暴力模拟,不然时间复杂度过高就超时了,要推公式,注意 n = m = 1 n=m=1 n=m=1时的特判
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
void solve()
{
int n, m;
cin >> n >> m;
if (m == 1 && n != 1)
{
cout << -1 << endl;
return;
}
if (m == n)
{
cout << 1 << endl;
return;
}
cout << ((n - 1) / (m - 1) + ((n - 1) % (m - 1) != 0)) * 2 - 1 << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
J题:
题目大意:有a,b两个数组,从中选n个数组成一圈,要确保选的b数组中的数不能两两相连,求最大值,不行就输出-1。
做法:用贪心加前缀和,可以先给a,b数组从大到小排个序,然后算一下前缀和。接着遍历a数组可选用数的个数,把不符合的跳过,符合的每种情况算一下取个max。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 1e4 + 10;
int a[N], b[N];
int sum1[N], sum2[N];
void solve()
{
int A, B, n;
cin >> A >> B >> n;
for (int i = 1; i <= A; i++)
{
cin >> a[i];
}
for (int i = 1; i <= B; i++)
{
cin >> b[i];
}
sort(a + 1, a + A + 1, [&](int c, int d)
{ return c > d; });
sort(b + 1, b + B + 1, [&](int c, int d)
{ return c > d; });
for (int i = 1; i <= A; i++)
{
sum1[i] = sum1[i - 1] + a[i];
}
for (int i = 1; i <= B; i++)
{
sum2[i] = sum2[i - 1] + b[i];
}
int maxb = min(n / 2, B);
if (A + maxb < n)
{
cout << -1 << endl;
return;
}
int ans = -1;
for (int i = 0; i <= A; i++)
{
int j = n - i;
if (j > maxb || j < 0)
continue;
ans = max(sum1[i] + sum2[j], ans);
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
A题:
九小时九个人九扇门
题目大意:给定n个数,求出在n个数中选出若干数的和的数字根为 1 9 1~9 1 9的个数。
做法:首先我们要知道数字根有一个性质:一个数的数字根就是这个数对9取模的结果,特别的,数字根为9取模为0。那么这道题就可以转换成一道01背包变形的问题。 f [ i ] [ j ] f[i][j] f[i][j]即是前i个数求和的数字根为j的结果。时间复杂度为 O ( 9 ∗ n ) O(9*n) O(9∗n)。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 998244353;
const int N = 1e5 + 10;
int f[N][10];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
f[0][0] = 1;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
x %= 9;
for (int j = 0; j < 9; j++)
{
f[i][(j + x) % 9] = (f[i][(j + x) % 9] + f[i - 1][j]) % mod;
f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
}
}
for (int i = 1; i < 9; i++)
{
cout << f[n][i] << " ";
}
cout << f[n][0] - 1 << endl;
return 0;
}
H题:
牛牛看云
题目大意:给定 n ( n ≤ 1 e 6 ) n(n\leq1e6) n(n≤1e6)个数,每个数 ≤ 1000 \leq1000 ≤1000,求下面这个式子:
∑ i = 1 n ∑ j = i n ∣ a i + a j − 1000 ∣ \sum_{i=1}^{n} \sum_{j=i}^{n} |a_i+a_j-1000| i=1∑nj=i∑n∣ai+aj−1000∣
做法:暴力循环n肯定不行,要 t l e tle tle了,所以换个角度,因为每个数 ≤ 1000 \leq1000 ≤1000,所以遍历 0 − 1000 0-1000 0−1000,在输入的时候统计每个数出现的次数,枚举数的时候,不同的数对 ( i , j ) (i,j) (i,j)直接相乘,相同的数对 ( i , j ) (i,j) (i,j)特殊算,为 c n t [ i ] + C c n t [ i ] 2 cnt[i]+C^{2}_{cnt[i]} cnt[i]+Ccnt[i]2,在乘以 ∣ a i + a j − 1000 ∣ |a_i+a_j-1000| ∣ai+aj−1000∣
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 1010;
ll cnt[N];
ll C(ll cnt)
{
return cnt * (cnt - 1) / 2ll;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
cnt[x]++;
}
ll ans = 0;
for (int i = 0; i <= 1000; i++)
{
for (int j = i; j <= 1000; j++)
{
ll add;
if (i == j)
add = C(cnt[i]) + cnt[i];
else
add = cnt[i] * cnt[j];
ans += add * abs(i + j - 1000);
}
}
cout << ans << endl;
return 0;
}
C题:
Baby’s first attempt on CPU
题目大意:一读一写两个语句中间要隔三个及以上的语句,问满足条件后要加几局空语句。
做法:维护一下每个语句的位置,并且每次要更新下位置,如果是一,位置不满足的话,就添加句子,再更新位置。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 105;
array<int, 5> a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i][1] >> a[i][2] >> a[i][3];
a[n][4] = i;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
a[i][4] = a[i - 1][4] + 1;
for (int j = 1; j <= 3; j++)
{
if (a[i][j] == 1)
{
if (a[i][4] - a[i - j][4] < 4)
{
ans += 4 - (a[i][4] - a[i - j][4]);
a[i][4] += 4 - (a[i][4] - a[i - j][4]);
}
}
}
}
cout << ans << endl;
return 0;
}
F题:
中位数切分
题目大意:给n个数和m,求把n个数切成若干段,保证每段的中位数都 ≥ m \geq m ≥m,问能切成几段,若不能切,则输出-1。
做法: O ( n ) O(n) O(n)的做法,统计 ≥ m \geq m ≥m的个数 c n t 1 cnt1 cnt1, < m
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int a[N];
void solve()
{
int n, m;
cin >> n >> m;
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
if (x >= m)
cnt1++;
else
cnt2++;
}
int ans = cnt1 - cnt2;
if (ans <= 0)
cout << -1 << endl;
else
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
先写这么多,后面再补。
B题:
B站与各唱各的
题目大意:n个人,m句话,每个人可以选择唱或者不唱,求唱成功的句子的期望。
做法:对于单个句子分析,没唱成功的概率为 p n + ( 1 − p ) n p^n+(1-p)^n pn+(1−p)n,则成功的概率为 1 − ( p n + ( 1 − p ) n 1-(p^n+(1-p)^n 1−(pn+(1−p)n,要使这个式子值最大,通过肉眼看感觉或者求导做,可以得出当 p = 1 2 p=\frac 12 p=21时最大,所以再乘个m即可,注意要乘逆元。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int p = 1e9 + 7;
int qmi(int m, int k)
{
int res = 1 % p, t = m;
while (k)
{
if (k & 1)
res = (ll)res * t % p;
t = (ll)t * t % p;
k >>= 1;
}
return res;
}
int inv(int x)
{
return qmi(x, p - 2);
}
void solve()
{
int n, m;
cin >> n >> m;
cout << ((1ll * qmi(2, n) - 2) * inv(qmi(2, n)) % p * m % p) << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D题:
牛牛做数论
题目大意:给一个n,求 [ 2 , n ] [2,n] [2,n]中使 H ( x ) H(x) H(x)值最大和最小的数, H ( x ) = ϕ ( x ) x H(x)=\frac {\phi(x)}{x} H(x)=xϕ(x)。若n=1,则输出-1。
做法:数论题,求最小值即是求质数前缀积 ≤ n \leq n ≤n中最大的一个。求最大值就是 ≤ n \leq n ≤n的最大的质数。所以可以先打表前缀积预处理下。
代码如下:
/*
coder:sunshine
school:njupt
*/
#include
using namespace std;
#define endl '\n' //交互题删掉
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
int a[9] = {2, 6, 30, 210, 2310, 30030, 510510, 9699690, 223092870};
bool isprime(int x)
{
if (x < 2)
return false;
for (int i = 2; i <= x / i; i++)
if (x % i == 0)
return false;
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
if (n == 1)
{
cout << -1 << endl;
}
else
{
for (int i = 0; i < 9; i++)
{
if (i == 8)
{
cout << a[i] << " ";
}
else
{
if (a[i] <= n && a[i + 1] > n)
{
cout << a[i] << " ";
break;
}
}
}
while (!isprime(n))
{
n--;
}
cout << n << endl;
}
}
return 0;
}