2022牛客寒假算法基础集训营1 ABCDEFGHIJKL
原创
©著作权归作者所有:来自51CTO博客作者wx58438afac3cd5的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
- A.[九小时九个人九扇门](https://ac.nowcoder.com/acm/contest/23106/A)
- B.[炸鸡块君与FIFA22](https://ac.nowcoder.com/acm/contest/23106/B)
- C.[Baby's first attempt on CPU](https://ac.nowcoder.com/acm/contest/23106/C)
- D.[牛牛做数论](https://ac.nowcoder.com/acm/contest/23106/D)
- E.[炸鸡块君的高中回忆](https://ac.nowcoder.com/acm/contest/23106/E)
- F.[中位数切分](https://ac.nowcoder.com/acm/contest/23106/F)
- G.[ACM is all you need](https://ac.nowcoder.com/acm/contest/23106/G)
- H.[牛牛看云](https://ac.nowcoder.com/acm/contest/23106/H)
- I.[B站与各唱各的](https://ac.nowcoder.com/acm/contest/23106/I)
- J.[小朋友做游戏](https://ac.nowcoder.com/acm/contest/23106/J)
- K.[冒险公社](https://ac.nowcoder.com/acm/contest/23106/K)
- L.[牛牛学走路](https://ac.nowcoder.com/acm/contest/23106/L)
思路
首先观察数根的性质,令
表示数字
的数字根,那么有
,根据这个性质:
- 设
- 表示前
- 个人可以组合出
- 的方案数
- 第
- 个人被加入方案时:第
- 个人组合出
- 的状态下可以向第
- 个人组合出
- 的状态转移,也就是
- 第
- 个人不加入方案时:第
- 个人组合出
- 的状态下可以向第
- 个人组合出
- 的状态转移,也就是
- 注意从
- 开始枚举,因为第
- 个人本身可以作为一种方案存在
求数根可以直接应用性质:模
取根,注意
的倍数直接赋
,然后根据以上分析写即可。
Accepted Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, MOD = 998244353;
int dp[N][15], a[N];
inline int get(int x){
if(x < 10) return x;
else{
if(x % 9) return (x % 9);
else return 9;
}
}
inline void solve(){
int n = 0; cin >> n;
dp[0][0] = 1;
for(int i = 1; i <= n; i++){
int num = 0; cin >> num;
a[i] = get(num);
}
for(int i = 1; i <= n; i++){
for(int j = 0; j <= 9; j++) (dp[i][get(a[i] + j)] += dp[i - 1][j]) %= MOD;
for(int j = 0; j <= 9; j++) dp[i][j] = (dp[i][j] + dp[i - 1][j]) % MOD;
}
for(int i = 1; i <= 9; i++) cout << dp[n][i] << ' ';
}
signed main(){
solve();
return 0;
}
线段树/分块/倍增均可写。
线段树写法见:2022牛客寒假算法基础集训营1 B.炸鸡块君与FIFA22 线段树_HeartFireY的博客-CSDN博客
思路
考虑对第
条语句,如果与
条语句有关,那么需要在第
条语句后插入
条语句分隔。那么可以使用一个数组进行模拟,记
表示到原来第
条语句为止,消除读写相关性后的语句总数。那么显然对于第
条语句:
类似于一个小贪心
,但实际上就是在模拟整个过程。
Accepted Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int vis[N];
inline void solve(){
int n = 0; cin >> n;
for(int i = 1; i <= n; i++){
vis[i] = vis[i - 1];
int a, b, c; cin >> a >> b >> c;
if(a == 1) vis[i] = max(vis[i], vis[i - 1] + 3);
if(b == 1) vis[i] = max(vis[i], vis[i - 2] + 3);
if(c == 1) vis[i] = max(vis[i], vis[i - 3] + 3);
vis[i]++;
}
cout << vis[n] - n << endl;
}
signed main(){
//int t = 0; cin >> t;
//while(t--)
solve();
return 0;
}
思路
首先回顾欧拉函数:设
,其中
是质数,有
。
题目定义
,显然可以展开得到:
要求
的最值点,实际上就是求
的最值点:
对于最小值点,显然是所有素数前缀积中不超过
的最大值,最大值点则是
中最大的素数。那么直接暴力求累加即可。注意防止溢出,要边取模边乘。
Accepted Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5;
int a[N], book[N];
inline int get(int n){
bool flag = true;
while (flag){
flag = false;
for (int i = 2; i <= sqrt(n); i++){
if (!(n % i)){
flag = true, n--;
break;
}
}
}
return n;
}
void solve(){
int n = 0; cin >> n;
int cnt = 0, pos = get(n), ans = 1;
if (n == 1){ cout << -1 << endl; return; }
for (int i = 2; i <= 1000; i++){
int flag = 1;
for (int j = 2; j <= sqrt(i); j++){
if (i % j == 0){
flag = 0;
break;
}
}
if(flag) book[++cnt] = i;
}
for (int i = 1; i <= cnt; i++){
if (ans * book[i] > n) break;
ans *= book[i];
}
cout << ans << ' ' << pos << endl;
}
signed main(){
int t = 0; cin >> t;
while (t--) solve();
return 0;
}
思路
考虑只有第一次会进入
个人,以后每次减少
个人,每次减少需要两轮。但是如果刚好
时,不需要多出来一次。
注意特判KaTeX parse error: Undefined control sequence: \and at position 7: m = 1 \̲a̲n̲d̲ ̲n = 1的时候,是可以的。。。
Accepted Code
#include <bits/stdc++.h>
using namespace std;
inline void solve(){
int n, m; cin >> n >> m;
if(m == 1){
if(n == 1) cout << 1 << endl;
else cout << -1 << endl;
return;
}
int d1 = 1 + (n - m) / (m - 1), d2 = (n - m) % (m - 1) != 0;
if(n == m) cout << 1 << endl;
else cout << 2 * (d1 + d2) - 1 << endl;
}
signed main(){
int t = 0; cin >> t;
while(t--) solve();
return 0;
}
思路
直接统计所有大于等于
的数字个数,单独统计小于
的数字个数。
如果所有大于等于
的数字单独作为以一个区间,那么显然每个区间都符合中位数要求。如果将小于等于
的数字插入这些区间,那么显然一个小于等于
的数插入后要维持中位数大于等于
的性质,需要另外一个大于等于
的数字配合插入。
也就是说,插入该数字后,总区间数将减一。那么可以知道:
- 当所有大于等于
- 的数字个数少于小于
- 的数字个数时,将无法构成题目要求,此时输出
-1
- 当所有大于等于
- 的数字个数多于小于
- 的数字个数时,数目为大于等于
- 的数字个数减去小于
- 的数字个数,也就是每个小于
- 的数字插入时都会引起一次区间数目的减少。
Accepted Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline void solve(){
int n, m; cin >> n >> m;
int cnt1 = 0, cnt2 = 0;
for(int i = 0; i < n; i++){
int num; cin >> num;
if(num >= m) cnt1++;
else cnt2++;
}
if(cnt1 <= cnt2) cout << -1 << endl;
else cout << cnt1 - cnt2 << endl;
}
signed main(){
solve();
return 0;
}
赛时乱搞过了,十分迷惑
待补
思路
数据范围很**,所以直接开桶记录数字个数,然后遍历一下暴力加即可。
注意在求和时,由于求得是上三角矩阵,可以现求整个矩阵,将对角线
后整体
。
Accepted Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000 + 10;
int a[N];
inline void solve(){
int n = 0; cin >> n;
for(int i = 1; i <= n; i++){
int num = 0; cin >> num; a[num]++;
}
int ans = 0;
for(int i = 0; i <= 1000; i++){
ans += abs(i + i - 1000) * a[i]; //对角线多加一次
for(int j = 0; j <= 1000; j++)
ans += abs(i + j - 1000) * (a[i] * a[j]);
}
cout << ans / 2 << endl;
}
signed main(){
solve();
return 0;
}
思路
(通过万能队友)得结论为
。然后逆元搞一下就可以了。
Accepted Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 1e9 + 7;
int pow(int a, int b, int mod){
int ans = 1;
while (b){
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int getinv(int a, int mod){
return pow(a, mod - 2, mod);
}
inline void solve(){
int n, m; cin >> n >> m;
int ans = m * (1 + MOD - getinv(pow(2, n - 1, MOD), MOD)) % MOD;
cout << ans << endl;
}
signed main(){
int t = 0; cin >> t;
while (t--) solve();
return 0;
}
思路
首先考虑围成圆圈的限制:实际上就是在显示最多选择
个闹腾的的XPY,然后分别对两组小朋友排序后求前缀和,然后枚举安静小朋友的数量,不断地取
即可。
Accepted Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
inline bool cmp(int &a, int &b){ return a > b; }
inline void solve(){
int aa, bb, n; cin >> aa >> bb >> n;
for(int i = 1; i <= aa; i++) cin >> a[i];
for(int i = 1; i <= bb; i++) cin >> b[i];
if(aa < (n - n / 2)){ cout << -1 << endl; return; }
sort(a + 1, a + 1 + aa, cmp); sort(b + 1, b + 1 + bb, cmp);
for(int i = 1; i <= aa; i++) a[i] += a[i - 1], b[i] += b[i - 1];
int ans = -1;
for(int i = 0; i <= aa; i++){
if(n - i > min(n / 2, bb) || n - i < 0) continue;
ans = max(ans, a[i] + b[n - i]);
}
cout << ans << endl;
}
signed main(){
int t = 0; cin >> t;
while(t--) solve();
return 0;
}
先贴个队友博客:K. 冒险公社 (线性DP)_yezzz的博客-CSDN博客
再贴个自己的:2022牛客寒假算法基础集训营1 K.冒险公社 DP_HeartFireY的博客-CSDN博客
思路
边读入边取
即可。
Accepted Code
#include <bits/stdc++.h>
using namespace std;
inline void solve(){
int n = 0;cin >> n;
getchar();
double max1 = 0, p = 0;
long long x = 0, y = 0;
for (int i = 0; i < n; i++){
scanf("%c", &a[i]);
if (a[i] == 'L') x--;
else if (a[i] == 'R') x++;
else if (a[i] == 'U') y++;
else if (a[i] == 'D') y--;
p = sqrt(x * x + y * y);
max1 = max(p, max1);
}
printf("%.12lf\n", max1);
}
signed main(){
int t = 0; cin >> t;
while (t--) solve();
return 0;
}