2024年上海市大学生程序设计竞赛 部分题解
附上cf的链接:
The 2024 Shanghai Collegiate Programming Contest
A. 无线网络整点栅格统计
题意
在
思路
数据量(
对正方形的每个询问顶点(a, b),枚举对角顶点(c, d)
由已知的两点可得正方形的中点,根据另一条对角线与这条对角线垂直,得已知两点在x, y方向上的改变量与剩下两点在y,x方向上的改变量相同,可以算出剩下两个顶点的位置。
判断剩下两个顶点是否在区域内即可。
具体公式看代码
参考代码
void solve(){
cin >> n >> m;
for(int i = 0; i <= n; i++){
for(int j = 0; j <= m; j++){
for(int i2 = 0; i2 <= n; i2++){
for(int j2 = 0; j2 <= m; j2++){
int del_i = i - i2, del_j = j - j2;
if((abs(del_i) + abs(del_j)) % 2 == 1 || (i == i2 && j == j2)) continue;
int x1 = (i + i2 + del_j) / 2, y1 = (j + j2 - del_i) / 2,
x2 = (i + i2 - del_j) / 2, y2 = (j + j2 + del_i) / 2;
if(x1 >= 0 && x1 <= n && x2 >= 0 && x2 <= n && y1 >= 0 && y1 <= m && y2 >= 0 && y2 <= m) a[i][j]++;
}
}
}
}
for(int i = 0; i <= n; i++){
for(int j = 0; j <= m; j++){
cout << a[i][j] << " ";
}
cout << endl;
}
}
E. 无线软件日
题意
给定一个字符串,其中的字母最多能组合成几个单词 shanghai (大小写不敏感)
思路
签到题
把字符串转换成全小写再进行处理,统计目标字母的出现次数(s,h,a,n,g,i)取最小值
注意 h 和 a 的次数要除2
参考代码
void solve(){
int n; cin >> n;
string s; cin >> s;
for(int i = 0; i < n; i++){
if(s[i] >= 'A' && s[i] <= 'Z') s[i] = s[i] - 'A' + 'a';
}
vector<int> cnt(128);
int ans = 0;
for(int i = 0; i < n; i++) cnt[s[i]]++;
ans = min({cnt['a'] / 2, cnt['h'] / 2, cnt['s'], cnt['i'], cnt['g'], cnt['n']});
cout << ans << endl;
}
G. 象棋大师
题意
小万和小宁在玩一种很新的象棋。
棋盘的大小是
小宁有 m 只马,这些马的位置已知,马不能移动,但是一旦小万的卒进入到了小宁任何一只马的攻击范围时,小宁的卒会立即被吃掉。马的攻击范围与中国象棋规则相同,包含了“别马脚”的情况。
现在,小万希望将位于起点 (0, 0) 的卒,安全的移动到终点 (n, n) ,在这个过程中(包括起点和终点)不被任何一只马攻击到,请问他有多少种不同的走法?
注意到,小万可以在移动卒的过程中吃掉小宁的马。只要卒移动到小宁的马所在位置,这只马就会被吃掉。被吃掉的马不再能够攻击,并从棋盘上消失。
思路
P1002 [NOIP2002 普及组] 过河卒 的魔改版
在
细节比较多,结合代码的注释应该是可以理解的
参考代码
int T,n,m,q,k, ma[M][M], xx[M], yy[M];
int a[M][M][15];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1}; //马脚的位置
int mx[] = {2, 2, 1, -1, -2, -2, -1, 1};
int my[] = {-1, 1, 2, 2, 1, -1, -2, -2};//马的攻击位置,枚举时与马脚的位置对应
int f[M][M][N];
void solve(){
cin >> n >> m;
for(int i = 0; i <= n + 1; i++){
for(int j = 0; j <= n + 1; j++){
ma[i][j] = -1;
for(int s = 0; s < m; s++) a[i][j][s] = m; //初始化,代表该位置不是被某个马攻击的位置
}
}
for(int i = 0; i < m; i++){
int x, y; cin >> x >> y;
x++; y++;
ma[x][y] = i;
xx[i] = x; yy[i] = y;
}
for(int i = 0; i < m; i++){
for(int j = 0; j < 8; j++){
int nx = xx[i] + mx[j], ny = yy[i] + my[j],
jx = xx[i] + dx[j / 2], jy = yy[i] + dy[j / 2];
if(nx > 0 && nx <= n + 1 && ny > 0 && ny <= n + 1){
a[nx][ny][i] = ma[jx][jy];
// 记录马的某个攻击位置是否存在堵马脚的情况,没有就是-1,反之记录对应堵马脚的马
}
}
}
f[1][1][0] = 1;
// 初始状态,全0代表场上的马全活
for(int i = 1; i <= n + 1; i++){
for(int j = 1; j <= n + 1; j++){
for(int s = 0; s < (1 << m); s++){
bool flag = 0;
for(int k = 0; k < m; k++){
int foot = a[i][j][k];
if((s >> k) & 1 == 1 || foot == m) continue;
//如果当前状态在这个位置进行攻击的马死了或者位置为空,就是安全的
if(foot == -1 || ((s >> foot) & 1) == 1){
flag = 1; break;
}
//如果在这个位置进行攻击的马存活,并且没有被堵马脚或者堵马脚的马死了,卒就被吃了
}
if(flag) f[i][j][s] = 0;
else f[i][j][s] = (f[i][j][s] + f[i - 1][j][s] + f[i][j - 1][s]) % mod;
if(ma[i][j] != -1 && ((s >> ma[i][j]) & 1) == 0){
f[i][j][s | (1 << ma[i][j])] = (f[i][j][s | (1 << ma[i][j])] + f[i][j][s]) % mod;
f[i][j][s] = 0;
}
// 吃了某个位置的马之后,马的存活状态转移,原状态作废
}
}
}
int ans = 0;
for(int s = 0; s < (1 << m); s++) ans = (ans + f[n + 1][n + 1][s]) % mod;
cout << ans << endl;
}
J. 极简合数序列
题意
对正整数数组
思路
答案只可能是
证明:
1.除2以外的偶数均为合数,任意两个偶数之和必为合数;
2.任意两个奇数之和为偶数;
3.奇数与偶数的和可当作奇数考虑
因此合数区间的长度存在上限,直接遍历判断即可
参考代码
bool check(int x){
for(int i = 2; i * i <= x; i++){
if(x % i == 0) return false;
}
return true;
}
void solve(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int l = 0; l < 4; l++){
for(int i = 1; i + l <= n; i++){
int sum = 0;
for(int j = i; j <= i + l; j++) sum += a[j];
if(!check(sum)){
cout << l << endl;
return;
}
}
}
cout << "-1" << endl;
}
L. 扩散模型
题意
一棵以噪声隐表示为根的扩散树,
在这个问题中,我们会标记
为了解决这个问题,有
出于效率上的考量,我们希望选用尽可能少的候选词,使得隐表示从根节点出发最终一定能走到一个被标记的叶子结点。
思路
原题废话很多,不过读懂就知道本题是树形dp
用
初始时有
考虑状态转移方程,节点
因此其消耗由所有子节点
当节点
从根节点1出发遍历,时间复杂度
参考代码
void solve(){
cin >> n >> m >> k;
vector<int> road(n + 1);
vector<vector<int>> g(n + 1), dp(n + 1, vector<int>(2, INF));
for(int i = 1; i <= n; i++){
int k; cin >> k;
for(int j = 1; j <= k; j++){
int v; cin >> v;
g[i].push_back(v);
}
}
for(int i = 1; i <= m; i++){
int u, v; cin >> u >> v;
road[v] = 1;
}
for(int i = 1; i <= k; i++){
int t; cin >> t;
dp[t][0] = dp[t][1] = 0;
}
auto dfs = [&] (auto self, int u) -> void{
if(g[u].empty()) return;
int sum = 0;
for(auto v : g[u]){
self(self, v);
sum += min(dp[v][0], dp[v][1]);
if(road[v]) dp[u][1] = min(dp[u][1], min(dp[v][0], dp[v][1]) + 1);
}
dp[u][0] = min(dp[u][0], sum);
};
dfs(dfs, 1);
cout << (min(dp[1][0], dp[1][1]) == INF ? -1 : min(dp[1][0], dp[1][1])) << endl;
}
M. 不共戴天
题意
水面上依次排布着
咪蛤有
小夜鹭有
如果存在两片荷叶