这是正式开始练习团队赛的的第一场。本场难度适中,但是教育意义很好。对去年的我们很有挑战,赛后认为对当时没太大意义没补。本次打完之后收获颇多。在vp的过程中第5、6题均有出现题目理解错误的情况,需调整!!
本场链接:Attachments - 2025 National Invitational of CCPC (Zhengzhou)
Problem F. 幻形之路
本题是个搜索问题,赛时思路还是比较清晰。开始通过推导我想到标记后二重暴力取最小值,只是我们都在担心可能会超时。前面的还没敲完我又提议使用多源BFS,因为没看清题意导致一直错误,后边也没返回使用暴力的方法。
-
先从左上搜索,把左上#放到队列中(如果搜通直接0)
-
从右下搜索,将右下#进行标记
-
之后有两种处理方式
- 多源BFS,如果碰到右下#就直接弹出
- 双重循环暴力搜索两边的点取距离最小值
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
#define endl '\n'
#define pb push_back
#define all(x) x.begin(),x.end()
// const int mod = 998244353;
const int mod = 1e9+7;
const int INF = 1e18;
const int N = 1e6+5;
int n,m;
string s;
int dx[]={0,-1,0,1};
int dy[]={-1,0,1,0};
struct p
{
int st;
int x,y;
};
void solve()
{
cin >> n >> m;
vector<vector<char>> a(n+5,vector<char>(m+5));
vector<vector<bool>> vi(n+5,vector<bool>(m+5));
auto mp=vi; // 标记后#
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++) cin >> a[i][j];
queue<p> q,q1;
q.push({1,1,1});// 左上开始搜
while(q.size())
{
auto cu=q.front(); q.pop();
int st=cu.st,x=cu.x,y=cu.y;
if(x==n&&y==m)//搜通直接退出
{
cout << 0 << endl;
return;
}
if(a[x][y]=='#')//左上的源点放入q1
{
q1.push({1,x,y});
continue;
}
for(int i=0; i<4; i++)
{
int tx=x+dx[i];
int ty=y+dy[i];
if(tx<1||ty<1||tx>n||ty>m||vi[tx][ty]) continue;
vi[tx][ty]=1;
q.push({st+1,tx,ty});
}
}
for(int i=1; i<=n; i++) fill(all(vi[i]),0);
q.push({1,n,m});//右下开始搜
while(q.size())
{
auto cu=q.front(); q.pop();
int st=cu.st,x=cu.x,y=cu.y;
if(a[x][y]=='#')//标记终点#
{
mp[x][y]=1;
continue;
}
for(int i=0; i<4; i++)
{
int tx=x+dx[i];
int ty=y+dy[i];
if(tx<1||ty<1||tx>n||ty>m||vi[tx][ty]) continue;
vi[tx][ty]=1;
q.push({st+1,tx,ty});
}
}
for(int i=1; i<=n; i++) fill(all(vi[i]),0);
while(q1.size())// 多源BFS
{
auto cu=q1.front(); q1.pop();
int st=cu.st,x=cu.x,y=cu.y;
if(mp[x][y])//找到右下#
{
cout << st << endl;
return ;
}
for(int i=0; i<4; i++)
{
int tx=x+dx[i];
int ty=y+dy[i];
if(tx<1||ty<1||tx>n||ty>m||vi[tx][ty]) continue;
vi[tx][ty]=1;
q1.push({st+1,tx,ty});
}
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T=1;
cin >> T;
while(T--) solve();
return 0;
}
Problem M. 川陀航空学院
- 并查集模板。
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
#define endl '\n'
#define pb push_back
#define all(x) x.begin(),x.end()
// const int mod = 998244353;
const int mod = 1e9+7;
const int INF = 1e18;
const int N = 1e6+5;
int n,m;
string s;
vector<int> p(N),sz(N);
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void unite(int x, int y)
{
x=find(x),y=find(y);
if(x==y) return ;
if(sz[x]>sz[y]) swap(x,y);
p[x]=y;
sz[y]+=sz[x];
}
void solve()
{
cin >> n >> m;
int ans=0;
for(int i=1; i<=n; i++) p[i]=i,sz[i]=1;
for(int i=0; i<m; i++)
{
int u,v;
cin >> u >> v;
if(find(u)==find(v)) ans++;
else unite(u,v);
}
int cnt=0;
for(int i=1; i<=n; i++) cnt+=(p[i]==i);//联通块个数
ans+=cnt-1;
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T=1;
// cin >> T;
while(T--) solve();
return 0;
}
Problem H. 树论函数
- 公式推导、打表
原公式: f ( n ) = f ( a ) ⋅ f ( b ) f(n)=f(a)·f(b) f(n)=f(a)⋅f(b)。
f ( a ) ⋅ f ( a + 1 ) = a ( a + 1 ) ( a + 1 ) ( a + 2 ) f(a)·f(a+1)=a(a+1)(a+1)(a+2) f(a)⋅f(a+1)=a(a+1)(a+1)(a+2)
= a ( a + 2 ) ( a + 1 ) ( a + 1 ) =a(a+2)(a+1)(a+1) =a(a+2)(a+1)(a+1)
= ( a 2 + 2 a ) ( a 2 + 2 a + 1 ) =(a^2+2a)(a^2+2a+1) =(a2+2a)(a2+2a+1)
设 b = a 2 + 2 a b=a^2+2a b=a2+2a
有 f ( b ) = f ( a ) ⋅ f ( a + 1 ) f(b)=f(a)·f(a+1) f(b)=f(a)⋅f(a+1)
则任意相邻节点都有某个点和他们相连接,所以所有的点都是相连的
- 既然所有都相连,直接输出区间长度即可
Problem E. 双生魔咒
- 字符串哈希
本题可用字符串/字典树解决,在这介绍字符串哈希的解法。
-
要使前缀尽量多,肯定要先进行排序。字符串的排序就是从头到尾对比相似度。
-
其次涉及到如何存的问题。
- 如果把所有字符串的所有前缀都存起来那么是会超内存的,并且字符串的比较也是很慢,还会超时间
字符串哈希会把字符串映射成对应的哈希值,存储数字一个只有8字节,并且比较操作也很快。
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
#define endl '\n'
#define pb push_back
#define all(x) x.begin(),x.end()
#define ull unsigned long long
// const int mod = 998244353;
const int mod = 1e9+7;
const int INF = 1e18;
const int N = 1e6+5;
int n,m;
string s;
const int P=131;
void solve()
{
cin >> n;
vector<string> a(2*n+1);
for(int i=1; i<=2*n; i++) cin >> a[i];
sort(a.begin()+1,a.end());
unordered_map<int,int> mp;
int cnt=0;
for(int i=1; i<=2*n; i+=2)
{
ull cu=0;
string k=' '+a[i];
for(int j=1; j<k.size(); j++)
{
cu=cu*P+k[j];
mp[cu]++;
}
}
for(int i=2; i<=2*n; i+=2)
{
ull cu=0;
string k=' '+a[i];
for(int j=1; j<k.size(); j++)
{
cu=cu*P+k[j];
if(mp.count(cu)) cnt+=mp[cu];
}
}
cout << cnt << endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T=1;
// cin >> T;
while(T--) solve();
return 0;
}