2023年 ZZU ACM 招新赛暨选拔赛题解
迪丽瓦拉
2025-05-31 01:25:21
0

A. NANA与字符串—找规律

题目链接

注意题目中 字符串中只有a,b两个字符
因此只要找到左右两端点字符相同的子串,这个子串一定回文,这里不在证明
求长为偶数回文串数量,就等于求相同的两个字符,而下标奇偶性不同的数对数量,比如0, 1 两个下标都是‘a’,这是偶数回文
同理 求长度为奇数回文,等于求下标奇偶性相同的数对数量
求奇数时需要注意,因为奇偶性相同是同类,求数对数量即求组合数n*(n - 1)/ 2 最后加上每个单个的字符
偶数不需要除以2是因为奇偶性不同,不会重复

#include 
#include 
using namespace std;
#define int long long
signed main()
{string s; cin >> s;int a1 = 0, a2 = 0, b1 = 0, b2 = 0;for(int i = 0; i < s.size(); i ++){a1 += (s[i] == 'a' && (i & 1));a2 += (s[i] == 'a' && !(i & 1));b1 += (s[i] == 'b' && (i & 1));b2 += (s[i] == 'b' && !(i & 1));}int res = a1 * a2 + b1 * b2;cout << res << ' ';	res = (a1 - 1) * a1 / 2 + (a2 - 1) * a2 / 2 + (b1 - 1) * b1 / 2 + (b2 - 1) * b2 / 2;cout << res + s.size() << '\n';
}

B

题目链接


C. NANA去上课 — 简单数学

需要记录上一步处在哪个位置
然后判断如果是同一侧移动距离就是abs(x1 - x2)
如果不同就是x1 + x2

#include 
#include 
using namespace std;
#define int long long
signed main()
{int n; cin >> n;char s, pres; int x, prex; cin >> pres >> prex;int res = prex;for(int i = 2; i <= n; i ++){cin >> s >> x;if(pres != s) res += x + prex;else res += abs(prex - x);pres = s, prex = x;}res += prex;cout << res << '\n';
}

D. NANA在夜市 — bfs

倒着思考,从终点往周围扩展,判断能否到达,把能到达的点放入队列,接着扩展
这里判断时需要注意从能到达的点往外扩展时 方向是反着的,判断能否到达需要反着判断

#include 
#include 
using namespace std;
const int N = 1010;
char g[N][N];
bool st[N][N];
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, 1, -1};signed main()
{int n, m; cin >> n >> m;for(int i = 0; i < n; i ++) cin >> g[i];int sx, sy;// 找到终点位置 for(int i = 0; i < n; i ++)for(int j = 0; j < m; j ++)if(g[i][j] == 'O')sx = i, sy = j;queue> q;q.push({sx, sy});st[sx][sy] = true;int res = 1;while(q.size()){int x = q.front().first;int y = q.front().second;q.pop();for(int i = 0; i < 4; i ++){int ax = x + dx[i];int ay = y + dy[i];if(ax < 0 || ax >= n || ay < 0 || ay >= m || st[ax][ay]) continue;if((i == 0 && g[ax][ay] == 'D') || (i == 1 && g[ax][ay] == 'U') || (i == 2 && g[ax][ay] == 'L') || (i == 3 && g[ax][ay] == 'R')) {q.push({ax, ay});st[ax][ay] = true;res ++;}}}cout << res << '\n';}

E.


F. NANA 的排名 — 二分+排序

先按照每个人的最低分加入到总成绩,用两个数组记录,一个原始数组,一个按总成绩从大到小排序的数组
遍历每一个人,用二分在在已经排序的数组里找到比它的数的数量就是排名
这里不需要考虑原来加入的自己,因为按最高分加入一定比最低分加入高

#include 
#include 
#include 
using namespace std;
struct Node
{int l, r;int sum;
};
bool cmp(Node a, Node b)
{return a.sum > b.sum;
}
signed main()
{int n; scanf("%d", &n);vector vec, pre;	for(int i = 0, l, r, x, sum; i < n; i ++){scanf("%d %d", &l, &r);sum = 0;for(int j = 0; j < 5; j ++){scanf("%d", &x);sum += x;}sum += l;vec.push_back({l, r, sum});pre.push_back({l, r, sum});}sort(vec.begin(), vec.end(), cmp);vector res;for(int i = 0; i < n; i ++){int sum = pre[i].sum + pre[i].r - pre[i].l;int l = 0, r = n;while(l < r){int mid = l + r >> 1;if(vec[mid].sum <= sum) r = mid;else l = mid + 1;}cout << l + 1 << '\n';}
}

G. NANA看天鹅舞会 — 贪心

最小的天鹅数黑白配对
剩下的相同2只配对
如果剩下的是奇数 减去c

#include 
using namespace std;
#define int long long
signed main()
{int n, m, a, b, c;cin >> n >> m >> a >> b >> c;if(n > m) swap(n, m);int res = n * a;m -= n;res += (m / 2) * b;if(m & 1) res -= c;cout << res << '\n';
}

H. NANA去集训 — 虚拟源点 + 堆优化的dijkstra

题目链接

这是一个很经典虚拟源点的模板题
需要将问题转化为单源最短路问题
因为最后还需要返回,因此路程距离直接乘2
对于每个答案,相当于求虚拟源点0 到点 i 的最短距离

#include 
#include 
#include 
#include 
using namespace std;
#define int long long
const int N = 200010;
vector> g[N];
int d[N];
bool vis[N];
signed main()
{int n, m;cin >> n >> m;for(int i = 1, a, b, w; i <= m; i ++){cin >> a >> b >> w;g[a].push_back({b, w * 2});g[b].push_back({a, w * 2});}for(int i = 1, x; i <= n; i ++){cin >> x;g[0].push_back({i, x});}priority_queue, vector>, greater>> q;for(int i = 1; i <= n; i ++) d[i] = 1e18;d[0] = 0;q.push({0, 0});while(q.size()){int u = q.top().second;q.pop();if(vis[u]) continue;vis[u] = true;for(auto [v, w] : g[u]){if(d[v] > d[u] + w){d[v] = d[u] + w;q.push({d[v], v});}}}for(int i = 1; i <= n; i ++)cout << d[i] << " ";
}

I. NANA做胡辣汤 — 滑动窗口

先把处理好的全加起来
用hh记录长度为k窗口的左端点,遍历右端点每次求出长度为k的区间中的没处理好的调料数量
每次更新res

#include 
using namespace std;
const int N = 100010;
#define int long long
int a[N], b[N];
signed main()
{int n, k; cin >> n >> k;for(int i = 0; i < n; i ++) cin >> a[i];for(int i = 0; i < n; i ++) cin >> b[i];int hh = 0, sum = 0;for(int i = 0; i < n; i ++)if(b[i]) sum += a[i];// 先处理第一个窗口 for(int i = 0; i < k; i ++)if(!b[i]) sum += a[i];int res = sum;for(int i = k; i < n; i ++){if(!b[i - k]) sum -= a[i - k];if(!b[i]) sum += a[i];res = max(sum, res);}cout << res << '\n';
}

J. NANA与她的朋友们 — 双指针

题目链接

k和ai很大,但是数只有1e5个,很容易想到离散化
因为每次影响的只有最大值和最小值,就可以直接看 最大值 和 最小值分别对应的数量哪个小
每次用k减去 那个小的,维护一个双指针,直到k小于0,或者双指针相遇

#include 
#include 
#include 
#include 
using namespace std;
#define int long long
map mp;
signed main()
{int n, k, x; scanf("%lld %lld",&n, &k);vector vec;for(int i = 0; i < n; i ++){scanf("%lld", &x);if(!mp.count(x)) vec.push_back(x);mp[x] ++;}sort(vec.begin(), vec.end());int hh = 0, tt = vec.size() - 1;while(hh < tt){int l = mp[vec[hh]];int r = mp[vec[tt]];if(l < r){int num = vec[hh + 1] - vec[hh];int cnt = l;if(k >= cnt * num){k -= cnt * num;mp[vec[hh + 1]] += mp[vec[hh]];hh ++;	} else{int t = k / cnt;k -= t * cnt;int res = vec[tt] - vec[hh] - t;cout << res << '\n';return 0;}}else{int num = vec[tt] - vec[tt - 1];int cnt = r;if(k >= cnt * num){k -= cnt * num;mp[vec[tt - 1]] += mp[vec[tt]];tt --;}else{int t = k / cnt;k -= t * cnt;int res = vec[tt] - t - vec[hh];cout << res << '\n';return 0;}}}cout << 0 << '\n';
}

K. NANA在郑州 — 小模拟

#include 
using namespace std;
#define int long long
int val[] = {1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 4};
signed main()
{int n;cin >> n;// kong geint res = n - 1;while(n --){string s; cin >> s;for(int i = 0; i < s.size(); i ++) res += val[s[i] - 'a'];}cout << res << '\n';
}

相关内容