vp 2025冬季PAT甲级
前言
考研初试刚结束不久,感觉初试考的不佳,为了面试有更高的分数,决定年后去考PAT甲级,于是今天vp了一下刚结束不久的2025冬季PAT甲级。总体来说,这场的强度不算大,没有卡各种算法和数据结构的时间复杂度,唯一值得说道的就是第一题的题目太长了,考的时候读题读的晕过去了。
题目总览


题目细览
第1题 A-1 Auto-scaling

思路
题目很长,其实只看样例和提示就可以知道要我们干啥了,模拟一下即可。
先处理第二行要输出的答案C[i],前S-1个C[i]就是输入的c[i]本身,后续的C[i]就应该是前S个c[i]/T再累加起来(前S个中包含自己),这里我们可以使用滑动窗口或者前缀和来降低计算的复杂度,我这里用的是滑动窗口,具体实现见下方代码。
再考虑第一行要输出的答案,前s-1个答案就是max(1,C[i]/T),因为至少要使用一台服务器,后续的需要分类讨论,看(C[i]-C[i-s+1])/s是否大于等于T*2,若大于等于,那么答案应该是上一次的答案加上(C[i]-C[i-s+1])/(s*T),否则答案应该为max(1,C[i]/T)。因为有可能需要使用到上一次输出的答案,因此我们使用一个last变量来存上一次输出的答案,每次输出完当前答案,就更新一下last。
我的AC代码
#include <bits/stdc++.h>
using i64 = long long;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n,T,S,s;
std::cin >> n >> T >> S >> s;
std::vector<int> c(n+1);
for(int i = 1; i <= n; i++){
std::cin >> c[i];
}
std::vector<int> C(n+1);
int sum = 0;
for(int i = 1; i <= S-1; i++){
sum += c[i]/S;
}
for(int i = 1; i <= n; i++){
if(i < S){
C[i] = c[i];
}else{
sum += c[i]/S;
sum -= c[i-S]/S;
C[i] = sum;
}
}
int last = -1;
for(int i = 1; i <= n; i++){
if(i < s){
std::cout << std::max(1,C[i]/T) << " \n"[i==n];
last = std::max(1,C[i]/T);
}else{
int diff = C[i]-C[i-s+1];
int t = diff/s;
if(t >= T*2){
std::cout << "*" << last+diff/(s*T) << " \n"[i==n];
last = last+diff/(s*T);
}else{
std::cout << std::max(1,C[i]/T) << " \n"[i==n];
last = std::max(1,C[i]/T);
}
}
}
for(int i = 1; i <= n; i++){
std::cout << C[i] << " \n"[i==n];
}
}
第2题 A-2 Puzzle

思路
我们首先找到第一行第一列要填的数,然后从这个位置开始dfs,dfs中枚举四个方向,若没有越界且之前没有填好,那么就填上去,dfs结束后输出即可。而且,我们发现要找第一行第一列要填的数,只需要找某个数的left和up同时为0的数即可。
我的AC代码
#include <bits/stdc++.h>
using i64 = long long;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n,m;
std::cin >> n >> m;
auto ans = std::vector(n+1,std::vector<int>(m+1));
std::unordered_map<int,std::vector<int>> map;
for(int i = 0; i < n*m; i++){
int num;
std::cin >> num;
for(int j = 0; j < 4; j++){
int x;
std::cin >> x;
map[num].push_back(x);
}
}
std::vector<std::vector<bool>> vis(n+1,std::vector<bool>(m+1));
for(auto [u,v]:map){
if(v[0]==0 && v[2]==0){
ans[1][1] = u;
vis[1][1] = true;
break;
}
}
auto dfs = [&](auto &&self,int x,int y)->void {
int num = ans[x][y];
for(int i = 0; i < 4; i++){
int to = map[num][i];
if(to == 0){
continue;
}
if(i == 0){
if(vis[x-1][y]){
continue;
}
ans[x-1][y] = to;
vis[x-1][y] = true;
self(self,x-1,y);
}else if(i == 1){
if(vis[x+1][y]){
continue;
}
ans[x+1][y] = to;
vis[x+1][y] = true;
self(self,x+1,y);
}else if(i == 2){
if(vis[x][y-1]){
continue;
}
ans[x][y-1] = to;
vis[x][y-1] = true;
self(self,x,y-1);
}else{
if(vis[x][y+1]){
continue;
}
ans[x][y+1] = to;
vis[x][y+1] = true;
self(self,x,y+1);
}
}
};
dfs(dfs,1,1);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
std::cout << ans[i][j] << " \n"[j==m];
}
}
}
第3题 A-3 Network Security

思路
第一行要输出的答案其实就是出度(入度也可,因为是双向边,但出度在使用vector存邻接表时,可以直接用adj[i].size()来表示节点i的出度)最大的点按升序输出,因此我们只需要遍历一遍1~n,计算出出度最大值max,然后再遍历1~n,若出度等于max则输出。
第二行其实就是考虑不包含隔离服务器的并查集的数量。
后续不同的并查集根据所含的最小节点号升序输出即可,相同的并查集输出同时也按升序处理。
我的AC代码
#include <bits/stdc++.h>
using i64 = long long;
struct DSU{
std::vector<int> f,sz;
DSU(int n){
f.resize(n+1);
std::iota(f.begin(),f.end(),0);
sz.assign(n+1,1);
}
int find(int x){
if(f[x] != x){
f[x] = find(f[x]);
}
return f[x];
}
bool merge(int u,int v){
int fu = find(u);
int fv = find(v);
if(fu == fv){
return false;
}
if(fv < fu){
std::swap(fu,fv);
}
sz[fu] += sz[fv];
f[fv] = fu;
return true;
}
};
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n,m;
std::cin >> n >> m;
std::vector<std::vector<int>> adj(n+1);
std::vector<int> a(n+1);
for(int i = 1; i <= n; i++){
std::cin >> a[i];
}
for(int mi = 0; mi < m; mi++){
int u,v;
std::cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
int max = 0;
for(int i = 1; i <= n; i++){
max = std::max(max,int(adj[i].size()));
}
bool isFirst = true;
for(int i = 1; i <= n; i++){
if(adj[i].size() == max && isFirst){
std::cout << i;
isFirst = false;
}else if(adj[i].size() == max){
std::cout << " " << i;
}
}
std::cout << "\n";
DSU dsu(n+1);
for(int i = 1; i <= n; i++){
if(a[i]){
continue;
}
for(int j = 0; j < adj[i].size(); j++){
if(a[adj[i][j]]){
continue;
}
int findi = dsu.find(i);
int findj = dsu.find(adj[i][j]);
if(findi == findj){
continue;
}
dsu.merge(findi,findj);
}
}
std::map<int,std::vector<int>> map;
for(int i = 1; i <= n; i++){
if(a[i]){
continue;
}
map[dsu.find(i)].push_back(i);
}
std::cout << map.size() << "\n";
for(auto [u,v]:map){
isFirst = true;
for(int i = 0; i < v.size(); i++){
if(isFirst){
std::cout << v[i];
isFirst = false;
}else{
std::cout << " " << v[i];
}
}
std::cout << "\n";
}
}
第4题 A-4 Prime Tree

思路
先来讲一下这个质数树的定义,对于深度为质数的节点,它的所有孩子节点数(这里它孩子的孩子也是它的孩子)也必须为质数,反之若深度不为质数,那么它的所有孩子节点数也必须不为质数。
因此,首先通过父亲为0找到根节点root,我们需要维护两个数组dep和son,分别表示节点的深度和它的所有孩子节点数,然后从根节点开始dfs,dfs中枚举边u->v,先令dep[v] = dep[u]+1,再dfs(v),最后累加孩子数son[u] = son[v]+1。
输出答案的时候,枚举i从1到n,依次输出i,dep[i],son[i]。然后判断dep[i]与son[i]的素性是否不同,若存在不同dep[i]和son[i]的素性,最后一行应该输出0,否则输出1。
我的AC代码
#include <bits/stdc++.h>
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n;
std::cin >> n;
std::vector<std::vector<int>> adj(n+1);
int root = -1;
for(int i = 1; i <= n; i++){
int fa;
std::cin >> fa;
if(fa == 0){
root = i;
}
adj[fa].push_back(i);
}
std::vector<int> dep(n+1),son(n+1);
dep[root] = 1;
auto dfs = [&](auto &&self,int u) ->void {
for(auto v:adj[u]){
dep[v] = dep[u]+1;
self(self,v);
son[u] += son[v]+1;
}
};
dfs(dfs,root);
bool isPrimeTree = true;
auto isPrime = [&](int x){
if(x < 2){
return false;
}
for(int i = 2; i <= x/i; i++){
if(x % i == 0){
return false;
}
}
return true;
};
for(int i = 1; i <= n; i++){
std::cout << i << " " << dep[i] << " " << son[i] << "\n";
bool isPrime1 = isPrime(dep[i]);
bool isPrime2 = isPrime(son[i]);
if((isPrime1&&!isPrime2) || (!isPrime1&&isPrime2)){
isPrimeTree = false;
}
}
std::cout << (isPrimeTree?1:0) << "\n";
}

浙公网安备 33010602011771号