目录
4.修复公路(2025“钉耙编程”中国大学生算法设计春季联赛3)
1.合并集合
一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。
现在要进行 m 个操作,操作共有两种:
M a b
,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;Q a b
,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 M a b
或 Q a b
中的一种。
输出格式
对于每个询问指令 Q a b
,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
1≤n,m≤10^5
输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes
代码展示
#include<iostream>
using namespace std;
int p[100010];
int find(int x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
void merge(int a,int b)
{
int x=find(a),y=find(b);
if(x!=y)
p[x]=y;
}
void query(int a,int b)
{
int x=find(a),y=find(b);
if(x==y)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
return ;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
p[i]=i;
while(m--)
{
char op;
cin>>op;
int a,b;
cin>>a>>b;
if(op=='M')
merge(a,b);
else
query(a,b);
}
}
2.连通块中点的数量
给定一个包含 n 个点(编号为 1∼n)的无向图,初始时图中没有边。
现在要进行 m 个操作,操作共有三种:
C a b
,在点 a 和点 b 之间连一条边,a 和 b 可能相等;Q1 a b
,询问点 a 和点 b 是否在同一个连通块中,a 和 b 可能相等;Q2 a
,询问点 a 所在连通块中点的数量;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 C a b
,Q1 a b
或 Q2 a
中的一种。
输出格式
对于每个询问指令 Q1 a b
,如果 a 和 b 在同一个连通块中,则输出 Yes
,否则输出 No
。
对于每个询问指令 Q2 a
,输出一个整数表示点 a 所在连通块中点的数量
每个结果占一行。
数据范围
1≤n,m≤10^5
输入样例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
输出样例:
Yes
2
3
代码展示
#include<iostream>
using namespace std;
int p[100010],s[100010];
int find(int x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
void merge(int a,int b)
{
int x=find(a),y=find(b);
if(x!=y)
{
p[x]=y;
s[y]+=s[x];
}
}
void query(int a,int b)
{
int x=find(a),y=find(b);
if(x==y)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
p[i]=i;
s[i]=1;
}
while(m--)
{
string op;
cin>>op;
int a,b;
if(op=="C")
{
cin>>a>>b;
merge(a,b);
}
else if(op=="Q1")
{
cin>>a>>b;
query(a,b);
}
else
{
cin>>a;
cout<<s[find(a)]<<endl;
}
}
}
3.食物链
动物王国中有三类动物 A,B,C这三类动物的食物链构成了有趣的环形。
A 吃 B,B 吃 C,C 吃 A。
现有 N 个动物,以 1∼N 编号。
每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法是 1 X Y
,表示 X 和 Y 是同类。
第二种说法是 2 X Y
,表示 X 吃 Y。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
- 当前的话与前面的某些真的话冲突,就是假话;
- 当前的话中 X 或 Y 比 N 大,就是假话;
- 当前的话表示 X 吃 X,就是假话。
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
输入格式
第一行是两个整数 N 和 K,以一个空格分隔。
以下 K 行每行是三个正整数 D,X,Y两数之间用一个空格隔开,其中 D 表示说法的种类。
若 D=1,则表示 X 和 Y 是同类。
若 D=2,则表示 X 吃 Y。
输出格式
只有一个整数,表示假话的数目。
数据范围
1≤N≤50000
0≤K≤100000
输入样例:
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
输出样例:
3
代码展示
#include<iostream>
using namespace std;
int p[150050];
int find(int x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
// a是同类域,a+n是捕食域,a+n+n是天敌域
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=3*n;i++)
p[i]=i;
int ans=0;
while(k--)
{
int t,a,b;
cin>>t>>a>>b;
if(a>n||b>n)
{
ans++;
continue;
}
else if(t==1)
{
if(find(a)==find(b+n)||find(a)==find(b+n+n))
{
ans++;
continue;
}
p[find(a)]=find(b);
p[find(a+n)]=find(b+n);
p[find(a+n+n)]=find(b+n+n);
}
else
{
if(find(a)==find(b)||find(a)==find(b+n))
{
ans++;
continue;
}
p[find(a)]=find(b+n+n);
p[find(a+n)]=find(b);
p[find(a+n+n)]=find(b+n);
}
}
cout<<ans<<endl;
}
4.修复公路(2025“钉耙编程”中国大学生算法设计春季联赛3)
有 n 座城市,依次坐落在一条直线上,相邻城市之间的距离为 1,且相邻城市之间原本有一条公路。现在,一场百年难遇的地震导致所有公路都被破坏了。
然而,每座城市都有一台空间传送机,可以从第 i 座城市传送到距离为 ai 的另一座城市,或者从距离为 ai 的城市传送到第 i 座城市(即从城市 i 可以传送到城市 i+ai 或 i−ai,或者反向传送,如果目标城市存在的话)。
现在,政府需要开展援助工作,希望能尽快实现从任意城市到任意城市的连通性。为此,政府决定修复部分公路。问至少修复多少长度的公路,才能满足上述要求?
输入格式/数据范围
第一行一个整数 T (1≤T≤1000),表示测试数据组数。
每组输入数据的第一行包含一个正整数 n (1≤n≤3×10^5),表示城市数量。
第二行包含 n 个整数 a1,a2,…,an(1≤ai≤n),表示每个城市的传送距离。
保证所有测试数据的 n 之和不超过 10^6。
输出格式
对于每组数据,输出一行一个整数表示需要最小需要修复公路的长度。
输入样例:
2
4
1 2 1 3
5
5 5 5 5 5
输出样例:
0
4
代码展示
#include<bits/stdc++.h>
using namespace std;
int p[300030];
int num;
int find(int x)
{
if(p[x]!=x)
p[x]=find(p[x]);
return p[x];
}
void merge(int a,int b)
{
int pa=find(a),pb=find(b);
if(pa!=pb)
{
p[pa]=pb;
num++;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
vector<int> a(n);
for(int i=0;i<n;i++)
{
p[i]=i;
cin>>a[i];
}
num=1;
for(int i=0;i<n;i++)
{
if(i+a[i]<n)
merge(i,i+a[i]);
if(i-a[i]>=0)
merge(i,i-a[i]);
}
cout<<n-num<<endl;
}
}