acwing——836 合并并查
一共有 nn 个数,编号是 1∼n1∼n,最开始每个数各自在一个集合中。
现在要进行 mm 个操作,操作共有两种:
M a b
,将编号为 aa 和 bb 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;Q a b
,询问编号为 aa 和 bb 的两个数是否在同一个集合中;输入格式
第一行输入整数 nn 和 mm。
接下来 mm 行,每行包含一个操作指令,指令为
M a b
或Q a b
中的一种。输出格式
对于每个询问指令
Q a b
,都要输出一个结果,如果 aa 和 bb 在同一集合内,则输出Yes
,否则输出No
。每个结果占一行。
数据范围
1≤n,m≤1051≤n,m≤105
输入样例:
4 5 M 1 2 M 3 4 Q 1 2 Q 1 3 Q 3 4
输出样例:
Yes No Yes
ac代码:
#include <stdio.h>
#define max 100010
int n,m,a,b;
char x;
int num[max];
int renew(int n){
int i = 1;
for(;i<=n;i++)
num[i] = i;
}
int find(int x){
if(num[x]!=x){
num[x] = find(num[x]);
}
return num[x];
}
void uni(int x,int y){
int f1,f2;
f1 = find(x);
f2 = find(y);
num[f1] = f2;
}
int main(){
scanf("%d %d",&n,&m);
int f1,f2;
renew(n);
while(m--){
getchar();
scanf("%c %d %d",&x,&a,&b);
if(x=='M'){
uni(a,b);
}
if(x=='Q'){
f1 = find(a);
f2 = find(b);
if(f1==f2){
printf("Yes\n");
}
else
printf("No\n");
}
}
return 0;
}
acwing——837 连接块中点的数量
给定一个包含 nn 个点(编号为 1∼n1∼n)的无向图,初始时图中没有边。
现在要进行 mm 个操作,操作共有三种:
C a b
,在点 aa 和点 bb 之间连一条边,aa 和 bb 可能相等;Q1 a b
,询问点 aa 和点 bb 是否在同一个连通块中,aa 和 bb 可能相等;Q2 a
,询问点 aa 所在连通块中点的数量;输入格式
第一行输入整数 nn 和 mm。
接下来 mm 行,每行包含一个操作指令,指令为
C a b
,Q1 a b
或Q2 a
中的一种。输出格式
对于每个询问指令
Q1 a b
,如果 aa 和 bb 在同一个连通块中,则输出Yes
,否则输出No
。对于每个询问指令
Q2 a
,输出一个整数表示点 aa 所在连通块中点的数量每个结果占一行。
数据范围
1≤n,m≤1051≤n,m≤105
输入样例:
5 5 C 1 2 Q1 1 2 Q2 1 C 2 5 Q2 5
输出样例:
Yes 2 3
/*解题思路:
解读题意:在原有的并查集基础上加上了查询该集合一共有多少元素的功能;
只需要用一个数组用来存储和修改即可;
当合并两个数的时候,偏向于一个数去连接分支,然后该数所携带的元素的和加上分支上的总数;
当需要到查询q2功能的时候,查找根节点,输出根节点所连接的点的总数即可
*/
ac代码:
#include <stdio.h>
#define max 100010
int n,m,a,b;
char x[3];
int num[max],good[max];
int renew(int n){
int i = 1;
for(;i<=n;i++){
num[i] = i;
good[i] = 1;
}
}
int find(int x){
if(num[x]!=x){
num[x] = find(num[x]);
}
return num[x];
}
void uni(int x,int y){
int f1,f2;
f1 = find(x);
f2 = find(y);
num[f1] = f2;
if(f1!=f2){
good[f2]+=good[f1];
}
}
int main(){
scanf("%d %d",&n,&m);
int f1,f2;
renew(n);
while(m--){
getchar();
scanf("%s",x);
if(x[0]=='C'){
scanf("%d %d",&a,&b);
uni(a,b);
}
else if(x[1]=='1'){
scanf("%d %d",&a,&b);
f1 = find(a);f2 = find(b);
if(f1==f2){
printf("Yes\n");
}
else{
printf("No\n");
}
}
else if(x[1]=='2'){
scanf("%d",&a);
f1 = find(a);
printf("%d\n",good[f1]);
}
}
return 0;
}
acwing-240 食物链
动物王国中有三类动物 A,B,CA,B,C,这三类动物的食物链构成了有趣的环形。
AA 吃 BB,BB 吃 CC,CC 吃 AA。
现有 NN 个动物,以 1∼N1∼N 编号。
每个动物都是 A,B,CA,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 NN 个动物所构成的食物链关系进行描述:
第一种说法是
1 X Y
,表示 XX 和 YY 是同类。第二种说法是
2 X Y
,表示 XX 吃 YY。此人对 NN 个动物,用上述两种说法,一句接一句地说出 KK 句话,这 KK 句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
- 当前的话与前面的某些真的话冲突,就是假话;
- 当前的话中 XX 或 YY 比 NN 大,就是假话;
- 当前的话表示 XX 吃 XX,就是假话。
你的任务是根据给定的 NN 和 KK 句话,输出假话的总数。
输入格式
第一行是两个整数 NN 和 KK,以一个空格分隔。
以下 KK 行每行是三个正整数 D,X,YD,X,Y,两数之间用一个空格隔开,其中 DD 表示说法的种类。
若 D=1D=1,则表示 XX 和 YY 是同类。
若 D=2D=2,则表示 XX 吃 YY。
输出格式
只有一个整数,表示假话的数目。
数据范围
1≤N≤500001≤N≤50000,
0≤K≤1000000≤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
ac代码:
#include <stdio.h>
#define max 100010
int n,m,x,y,sum=0,d;
int num[3*max];
int renew(int n){
int i = 1;
for(;i<=3*n;i++){
num[i] = i;
}
}
int find(int x){
if(num[x]!=x){
num[x] = find(num[x]);
}
return num[x];
}
void uni(int x,int y){
int f1,f2;
f1 = find(x);
f2 = find(y);
num[f1] = f2;
}
int main(){
scanf("%d %d",&n,&m);
int f1,f2;
renew(n);
while(m--){
scanf("%d %d %d",&d,&x,&y);
if(x>n||y>n){
sum++;
continue;
}
f1=find(x);
f2=find(y);
if(d==1){
if(f1==f2){
continue;
}
if(f1==find(y+n)||f1==find(y+n+n)){
sum++;
continue;
}
else{
uni(x,y);
uni(x+n,y+n);
uni(x+n+n,y+n+n);
}
}
else if(d==2){
if(x==y){
sum++;
continue;
}
if(f1==find(y+n)){
continue;
}
else if(f1==f2||f1==find(y+n+n)){
sum++;
continue;
}
else{
uni(x,y+n);
uni(x+n,y+n+n);
uni(x+n+n,y);
}
}
}
printf("%d\n",sum);
return 0;
}
acwing-789 数的范围
给定一个按照升序排列的长度为 nn 的整数数组,以及 qq 个查询。
对于每个查询,返回一个元素 kk 的起始位置和终止位置(位置从 00 开始计数)。
如果数组中不存在该元素,则返回
-1 -1
。输入格式
第一行包含整数 nn 和 qq,表示数组长度和询问个数。
第二行包含 nn 个整数(均在 1∼100001∼10000 范围内),表示完整数组。
接下来 qq 行,每行包含一个整数 kk,表示一个询问元素。
输出格式
共 qq 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回
-1 -1
。数据范围
1≤n≤1000001≤n≤100000
1≤q≤100001≤q≤10000
1≤k≤100001≤k≤10000输入样例:
6 3 1 2 2 3 3 4 3 4 5
输出样例:
3 4 5 5 -1 -1
思路:!!!在于二分查找的过程之中的中点的等于条件来查找左右边界
左边界不加1,右边界+1
ac代码:
//查找数的左右边界
//运用二分法查找需要是严格上升数列
#include <stdio.h>
#define N 100010
int n,m,x;
int q[N];
int main()
{
int i,l,r,mid;
scanf("%d %d",&n,&m);
for(i=0; i<n; i++) scanf("%d",&q[i]);
while(m--)
{
scanf("%d",&x);
l = 0; r = n -1;
while(l < r)
{
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时, 不必+ 1
mid = ( l + r )/2;
if(q[mid] >= x) r = mid;
else l = mid + 1;
}
if(q[l] != x)
printf("-1 -1\n");
else
{
printf("%d ",l);
l =0; r = n - 1;
while(l < r)
{
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时, 需要 + 1
mid = ( l + r + 1 ) / 2;
if(q[mid] <= x) l = mid;
else r = mid - 1;
}
printf("%d\n",l);
}
}
return 0;
}
acwing - 790 数的三次方根
给定一个浮点数 nn,求它的三次方根。
输入格式
共一行,包含一个浮点数 nn。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 66 位小数。
数据范围
−10000≤n≤10000−10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000
ac代码:
#include <stdio.h>
/*思路:
1. 二分查找,从数据mid = 0开始查找,
如果满足条件mid的三次方大于n或者小于0,逐渐缩小范围
2.输出最后查找出来的值就是n的三次方根
*/
double n,l,r,mid;
double s(double a){
return a*a*a;
}
int main()
{
l = -10000;
r = 10000;
scanf("%lf",&n);
while(r-l > 1e-8){
mid = (l+r)/2;
if(s(mid)>=n) r = mid;
else l = mid;
}
printf("%.6lf",l);
return 0;
}