蓝桥杯2023年第十四届c++B组省赛真题

寄了,300元买了两个脆脆鲨和一瓶水哈哈

可以在下面民间网站提交测试

蓝桥杯真题(更新至2023年) - 编程题库 - C语言网 (dotcpp.com)

教训 :(1)直接输出骗样例的别忘了先把题目的输入写上

(2)读清楚题目要求  (3)一定要return 0 ;啊啊啊啊

(4)devc++的输入窗口可以从属性哪里改成可复制(傻傻的自己敲案例)

(5)把思路想好了再动手,(别白白浪费时间)

(6)别忘开long long

下面是某大佬讲解的视频,讲的很清楚

2023年第十四届蓝桥杯软件类C/C++B组省赛真题全程手写代码完全讲解_哔哩哔哩_bilibili

试题 A: 日期统计(深搜+回溯)

【问题描述】

小蓝现在有一个长度为 100 的数组,数组中的每个元素的值都在 0 到 9 的

范围之内。数组中的元素从左至右如下所示:

5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2

7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1

0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3

现在他想要从这个数组中寻找一些满足以下条件的子序列:

1. 子序列的长度为 8;

2. 这个子序列可以按照下标顺序组成一个 yyyymmdd 格式的日期,并且

要求这个日期是 2023 年中的某一天的日期,例如 20230902,20231223。

yyyy 表示年份,mm 表示月份,dd 表示天数,当月份或者天数的长度只

有一位时需要一个前导零补充。

请你帮小蓝计算下按上述条件一共能找到多少个不同 的 2023 年的日期。

对于相同的日期你只需要统计一次即可。

#include
using namespace std;
const int MAX=2e5+10;
int arr[MAX],ans;
int months[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
/*dfs选入的数只需要判断56位月份对应的天数是否合理就行了 (1234 5678)*/
setmyset; //每一种日期只加入一次,因此用set去重 
bool check(int data)
{
    if(myset.count(data))return false;
    int month=(data/100%10+data/1000%10*10); //提取月份 先除保留高位,再%10取末位 
    if(month>12||month==0)return false;
    int days=data%10+data/10%10*10; //提取天数
    //注意这里月数和天数为0的情况 
    myset.insert(data);
    if(days<=months[month]&&days>0)return true; 
    return false;
} 
void dfs (int x,int index,int data)
{//x表示遍历到100个数哪一个的下标 ,index为选中了几个数的(0-8), data为选中index个数组成的日期
    
    if(x>=100)return ;    
    if(index==8)
    {
        if(check(data))//符合要求就加入答案 
        ans++;
    
        return ;
    }
    /*先把确定的几位剪枝*/
    if((index==0&&arr[x]==2)||(index==1&&arr[x]==0)||(index==2&&arr[x]==2)||(index==3&&arr[x]==3))
    {//前四位:data第1-4位必须是2013 
        dfs(x+1,index+1,data*10+arr[x]);
    }
    else if((index==4&&(arr[x]==0||arr[x]==1))||(index==5)||(index==6&&arr[x]>=0&&arr[x]<=3)||index==7)
    {//后四位:第四位月份首位必须是0/1 第五位随便  第六位那一天首位必须是0-3 第七位随便 
        dfs(x+1,index+1,data*10+arr[x]);
    }
    dfs(x+1,index,data);//不加入arr[x] (可能为上面不符合条件的 或回溯回来的) 
}
int main()
{
    for(int i=0;i<100;i++)
    cin>>arr[i]; 
    dfs(0,0,0);
    cout<<"----------\n";
    cout<

试题 B: 01 串的熵

蓝桥杯2023年第十四届c++B组省赛真题_第1张图片

#include
using namespace std;
const int MAX=2e5+10;
int main()
{
    double n=23333333;
    double ans=11625907.5798;
    double eps=1e-4;//浮点数比较方式:定义一个误差允许范围 
    for(int i=0;i<23333333/2;i++)
    {
        int j=n-i;
        double shang=-1.0*i*i/n*log2(i/n)-1.0*j*j/n*log2(j/n);
    //注意这两个1.0一定要乘上 (好像是不乘精度降低) 
        if(fabs(shang-ans)

 试题 C: 冶炼金属

【问题描述】
小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X 。这个
炉子有一个称作转换率的属性 V V 是一个正整数,这意味着消耗 V 个普通金
O 恰好可以冶炼出一个特殊金属 X ,当普通金属 O 的数目不足 V 时,无法
继续冶炼。
现在给出了 N 条冶炼记录,每条记录中包含两个整数 A B ,这表示本次
投入了 A 个普通金属 O ,最终冶炼出了 B 个特殊金属 X 。每条记录都是独立
的,这意味着上一次没消耗完的普通金属 O 不会累加到下一次的冶炼当中。
根据这 N 条冶炼记录,请你推测出转换率 V 的最小值和最大值分别可能是
多少,题目保证评测数据不存在无解的情况。
【输入格式】
第一行一个整数 N ,表示冶炼记录的数目。
接下来输入 N 行,每行两个整数 A B ,含义如题目所述。
【输出格式】
输出两个整数,分别表示 V 可能的最小值和最大值,中间用空格分开。
【样例输入】
3
75 3

53 2

59 2

【样例输出】
20 25
蓝桥杯2023年第十四届c++B组省赛真题_第2张图片

#include
using namespace std;
const int MAX=2e5+10;
int arr[MAX],output[MAX],n;
bool check(int mid)
{
	for(int i=0;ioutput[i])return false;
	}
	return true;
}
int main()
{
	
	int resmax=INT_MAX;
	cin>>n;
	for(int i=0;i>arr[i]>>output[i];	
		resmax=min(resmax,arr[i]/output[i]);
		//最大值就是arr[i]/output[i]的最小值 
	} 
	int left=0,right=resmax;
	while(left

试题 D: 飞机降落

【问题描述】
N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 T i 时刻
到达机场上空,到达时它的剩余油料还可以继续盘旋 D i 个单位时间,即它最早
可以于 T i 时刻开始降落,最晚可以于 T i + D i 时刻开始降落。降落过程需要 L i
个单位时间。
一架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不
能在前一架飞机完成降落前开始降落。
请你判断 N 架飞机是否可以全部安全降落。
【输入格式】
输入包含多组数据。
第一行包含一个整数 T ,代表测试数据的组数。
对于每组数据,第一行包含一个整数 N
以下 N 行,每行包含三个整数: T i D i L i
【输出格式】
对于每组数据,输出 YES 或者 NO ,代表是否可以全部安全降落。
【样例输入】
2
3
0 100 10
10 10 10
0 2 20
3
0 10 20
10 10 20
20 10 20
【样例输出】
YES
NO
【样例说明】
对于第一组数据,可以安排第 3 架飞机于 0 时刻开始降落, 20 时刻完成降
落。安排第 2 架飞机于 20 时刻开始降落, 30 时刻完成降落。安排第 1 架飞机
30 时刻开始降落, 40 时刻完成降落。
对于第二组数据,无论如何安排,都会有飞机不能及时降落。
【评测用例规模与约定】
对于 30 % 的数据, N 2
对于 100 % 的数据, 1 T 10 1 N 10 0 T i , D i , L i 10^ 5
本题贪心(每次让截止时间最早的下降,上一个下降完毕下一个马上降)不能AC 
举例 下例能完成,但用贪心完不成 
0 100 1
10 12 81
0 2 20
#include
using namespace std;
const int MAX=2e5+10;
struct Plane
{
	int T,D,L;
	int end;//飞机最晚下降时间 
}plane[MAX];
int n;
bool cmp(Plane p1,Plane p2)
{
	return p1.end>t;
	while(t--)
	{
		cin>>n;
		for(int i=0;i>T>>D>>L;
			plane[i].T=T;
			plane[i].D=D;
			plane[i].L=L;
			plane[i].end=T+D;
		} 
		
		int flag=1;
		sort(plane,plane+n,cmp);
		for(int i=0;i
#include
using namespace std;
const int MAX=2e5+10;
int T[MAX],D[MAX],L[MAX],used[MAX];
int flag,n;
/*curTime可降落时间(上一辆降落完毕时间),num是已降落数量 
本题采用N<=10,且类似全排列(排列每辆飞机降落顺序),可采用回溯 
*/
void backtracking(int curTime,int num)
{//枚举所有排列,能满足情况返回 
	if(num==n||flag)
	{
		flag=1;return ;
	}
	for(int i=0;iT[i]+D[i])continue; //这一辆降不了(到这一辆的截至时间上一辆还没降落完) 
		used[i]=1;
		backtracking(max(T[i],curTime)+L[i],num+1);
		//有可能curTime时间i还每到,所以i降落时间为 max(T[i],curTime)+L[i]
		used[i]=0;
	}
 } 
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(int i=0;i>T[i]>>D[i]>>L[i];
		} 
		flag=0;
		memset(used,0,n*4);
		backtracking(0,0);
		if(flag)cout<<"YES\n";
		else cout<<"NO\n";
	}
}

试题 E: 接龙数列

【问题描述】
对于一个长度为 K 的整数数列: A 1 , A 2 , . . . , A K ,我们称之为接龙数列当且
仅当 A i 的首位数字恰好等于 A i 1 的末位数字 (2 i K )
例如 12 , 23 , 35 , 56 , 61 , 11 是接龙数列; 12 , 23 , 34 , 56 不是接龙数列,因为 56
的首位数字不等于 34 的末位数字。所有长度为 1 的整数数列都是接龙数列。
现在给定一个长度为 N 的数列 A 1 , A 2 , . . . , A N ,请你计算最少从中删除多少
个数,可以使剩下的序列是接龙序列?
【输入格式】
第一行包含一个整数 N
第二行包含 N 个整数 A 1 , A 2 , . . . , A N
【输出格式】
一个整数代表答案。
【样例输入】
5
11 121 22 12 2023
【样例输出】
1
【样例说明】
删除 22 ,剩余 11 , 121 , 12 , 2023 是接龙数列。
【评测用例规模与约定】
对于 20 % 的数据, 1 N 20
对于 50 % 的数据, 1 N 10000
对于 100 % 的数据, 1 N 10^ 5 1 A i 10^ 9 。所有 A i 保证不包含前导 0
#include
using namespace std;
const int MAX=2e5+10;
int n,dp[10];
int main()
{
	cin>>n;
	/*dp[d]为以d这个数为结尾的最长接龙序列 (因为这个题全都是数字)
	所以对于每一个数 首部为x,尾部为y,dp[y]=max(dp[y],dp[x]+1) 
										不接到前面  接到前面 
	我用的类似子序列dp,只能ac五分之一,这个真的秀 ,跳出了序列,直接对数字进行dp 
	则最短删除长度为总长度减去最长接龙长度 
	*/
	int maxlen=0;
	for(int i=0;i>data;
		y=data%10;
		while(data)
		{//获取data高位 与低位 
			if(data/10==0)x=data;
			data/=10;	
		} 
		dp[y]=max(dp[y],dp[x]+1);
		maxlen=max(maxlen,dp[y]); 
	}
	cout<

试题 F: 岛屿个数

【问题描述】
小蓝得到了一副大小为 M × N 的格子地图,可以将其视作一个只包含字符
‘0’ (代表海水)和 ‘1’ (代表陆地)的二维数组,地图之外可以视作全部是海水,
每个岛屿由在上 / / / 右四个方向上相邻的 ‘1’ 相连接而形成。
在岛屿 A 所占据的格子中,如果可以从中选出 k 个不同的格子,使得
他们的坐标能够组成一个这样的排列: ( x 0 , y 0 ) , ( x 1 , y 1 ) , . . . , ( x k 1 , y k 1 ) ,其中
( x ( i +1) % k , y ( i +1) % k ) 是由 ( x i , y i ) 通过上 / / / 右移动一次得来的 (0 i k 1)
此时这 k 个格子就构成了一个 。如果另一个岛屿 B 所占据的格子全部位于
这个 内部,此时我们将岛屿 B 视作是岛屿 A 的子岛屿。若 B A 的子
岛屿, C 又是 B 的子岛屿,那 C 也是 A 的子岛屿。
请问这个地图上共有多少个岛屿?在进行统计时不需要统计子岛屿的数目。
【输入格式】
第一行一个整数 T ,表示有 T 组测试数据。
接下来输入 T 组数据。对于每组数据,第一行包含两个用空格分隔的整数
M N 表示地图大小;接下来输入 M 行,每行包含 N 个字符,字符只可能是
‘0’ ‘1’
【输出格式】
对于每组数据,输出一行,包含一个整数表示答案。
【样例输入】
2
5 5
01111
11001
10101
10001
11111
5 6
111111
100001
010101
100001
111111
【样例输出】
1
3
【样例说明】
对于第一组数据,包含两个岛屿,下面用不同的数字进行了区分:
01111
11001
10201
10001
11111
岛屿 2 在岛屿 1 内部,所以岛屿 2 是岛屿 1 的子岛屿,答案为 1
对于第二组数据,包含三个岛屿,下面用不同的数字进行了区分:
111111
100001
020301
100001
111111
注意岛屿 3 并不是岛屿 1 或者岛屿 2 的子岛屿,因为岛屿 1 和岛屿 2 中均没有
【评测用例规模与约定】
对于 30 % 的评测用例, 1 M , N 10
对于 100 % 的评测用例, 1 T 10 1 M , N 50
#include
using namespace std;
const int MAX=2e5+10;
int m,n,vis[55][55],used[55][55];
char graph[55][55];
int dx[]={-1,1,0,0,1,1,-1,-1};
int dy[]={0,0,1,-1,1,-1,1,-1};//标定八个方向 
void dfs(int i,int j)
{
	vis[i][j]=1;
	for(int dim=0;dim<4;dim++)
	{
		int x=i+dx[dim],y=j+dy[dim];
		if(x<0||x>=m||y<0||y>=n)continue;
		if(graph[x][y]=='0'||vis[x][y])continue;
		vis[x][y]=1;
		dfs(x,y);
	}
}
void init_used()
{
	for(int i=0;i=m||y<0||y>=n)continue;
		if(graph[x][y]!='0'||used[x][y])continue;
		used[x][y]=1;
		if(dfs_out(x,y))return true;//逃出去了返回 
	}
	return false;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		cin>>m>>n;
		for(int i=0;i>graph[i][j];
				vis[i][j]=0;
			}
		
		}
		int ans=0;
		for(int i=0;i

试题 G: 子串简写

【问题描述】
程序猿圈子里正在流行一种很新的简写方法:对于一个字符串,只保留首
尾字符,将首尾字符之间的所有字符用这部分的长度代替。例如 internation
alization 简写成 i18n Kubernetes (注意连字符不是字符串的一部分)简
写成 K8s , Lanqiao 简写成 L5o 等。
在本题中,我们规定长度大于等于 K 的字符串都可以采用这种简写方法
(长度小于 K 的字符串不配使用这种简写)。
给定一个字符串 S 和两个字符 c 1 c 2 ,请你计算 S 有多少个以 c 1 开头
c 2 结尾的子串可以采用这种简写?
【输入格式】
第一行包含一个整数 K
第二行包含一个字符串 S 和两个字符 c 1 c 2
【输出格式】
一个整数代表答案。
【样例输入】
4
abababdb a b
【样例输出】
6
【样例说明】
符合条件的子串如下所示,中括号内是该子串:
[abab]abdb
[ababab]db
[abababdb]
ab[abab]db
ab[ababdb]
abab[abdb]
【评测用例规模与约定】
对于 20 % 的数据, 2 K ≤ | S | ≤ 10000
对于 100 % 的数据, 2 K ≤ | S | ≤ 5 × 10^ 5 S 只包含小写字母。 c 1 c 2
是小写字母。
| S | 代表字符串 S 的长度
#include
using namespace std;
const int MAX=5e5+10;
int k,n,sum[MAX];
string s;
int main()
{
	/*本题使用后缀和 用sum记录[i,n)中c2的个数
	则从头遍历每一个c1,此c1产生的合规子串个数就是c1左边加上k-1,后面c1的个数 
	*/
	char c1,c2;
	cin>>k>>s>>c1>>c2;
	vectors1,s2;
	n=s.size();
	for(int i=n-1;i>=k-1;i--)
	{//sum[i]为从[i,n)有多少个c2 
		if(s[i]==c2)sum[i]=sum[i+1]+1;
		else sum[i]=sum[i+1]; 
	}
	long long ans=0;
	for(int i=0;i=MAX)temp=MAX-1;
			ans+=sum[temp];
		}
	}
	cout<

试题 H: 整数删除

【问题描述】
给定一个长度为 N 的整数数列: A 1 , A 2 , . . . , A N 。你要重复以下操作 K 次:
每次选择数列中最小的整数(如果最小值不止一个,选择最靠前的),将其
删除。并把与它相邻的整数加上被删除的数值。
输出 K 次操作后的序列。
【输入格式】
第一行包含两个整数 N K
第二行包含 N 个整数, A 1 , A 2 , A 3 , . . . , A N
【输出格式】
输出 N K 个整数,中间用一个空格隔开,代表 K 次操作后的序列。
【样例输入】
5 3
1 4 2 8 7
【样例输出】
17 7
【样例说明】
数列变化如下,中括号里的数是当次操作中被选择的数:
[1] 4 2 8 7
5 [2] 8 7
[7] 10 7
17 7
【评测用例规模与约定】
对于 20 % 的数据, 1 K < N 10000
对于 100 % 的数据, 1 K < N 5 × 10^ 5 0 A i 10^ 8
#include
using namespace std;
#define val first 
#define index second
const int MAX=5e5+10;
typedef long long ll; 
typedef pair PII;

priority_queuemyque;
ll arr[MAX];
int pre[MAX],nxt[MAX];
int n;
int main()
{
	/*用两个数组模拟双向链表 pre[i]为i结点上一个结点下标, nxt[i]为i下一个 
	用优先队列维护最小值  
	*/
	int k;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>arr[i];
		pre[i]=i-1;
		nxt[i]=i+1;
		myque.push({-arr[i],-i});//不想写cmp,所以插入负的val,则最大堆顶就是负的最大值,即最小值 
	} 
	pre[1]=-1;
	nxt[n]=-1;
	for(int j=0;j

试题 I: 景区导游

【问题描述】
某景区一共有 N 个景点,编号 1 N 。景点之间共有 N 1 条双向的摆
渡车线路相连,形成一棵树状结构。在景点之间往返只能通过这些摆渡车进行,
需要花费一定的时间。
小明是这个景区的资深导游,他每天都要按固定顺序带客人游览其中 K
景点: A 1 , A 2 , . . . , A K 。今天由于时间原因,小明决定跳过其中一个景点,只带游
客按顺序游览其中 K 1 个景点。具体来说,如果小明选择跳过 A i ,那么他会
按顺序带游客游览 A 1 , A 2 , . . . , A i 1 , A i +1 , . . . , A K , (1 i K )
请你对任意一个 A i ,计算如果跳过这个景点,小明需要花费多少时间在景
点之间的摆渡车上?
【输入格式】
第一行包含 2 个整数 N K
以下 N 1 行,每行包含 3 个整数 u , v t ,代表景点 u v 之间有摆渡
车线路,花费 t 个单位时间。
最后一行包含 K 个整数 A 1 , A 2 , . . . , A K 代表原定游览线路。
【输出格式】
输出 K 个整数,其中第 i 个代表跳过 A i 之后,花费在摆渡车上的时间。
【样例输入】
6 4
1 2 1
1 3 1
3 4 2
3 5 2
4 6 3
2 6 5 1
【样例输出】
10 7 13 14
【样例说明】
原路线是 2 6 5 1
当跳过 2 时,路线是 6 5 1 ,其中 6 5 花费时间 3 + 2 + 2 = 7
5 1 花费时间 2 + 1 = 3 ,总时间花费 10
当跳过 6 时,路线是 2 5 1 ,其中 2 5 花费时间 1 + 1 + 2 = 4
5 1 花费时间 2 + 1 = 3 ,总时间花费 7
当跳过 5 时,路线是 2 6 1 ,其中 2 6 花费时间 1 + 1 + 2 + 3 = 7
6 1 花费时间 3 + 2 + 1 = 6 ,总时间花费 13
当跳过 1 时,路线时 2 6 5 ,其中 2 6 花费时间 1 + 1 + 2 + 3 = 7
6 5 花费时间 3 + 2 + 2 = 7 ,总时间花费 14
【评测用例规模与约定】
对于 20 % 的数据, 2 K N 10 2
对于 40 % 的数据, 2 K N 10 4
对于 100 % 的数据, 2 K N 10 5 1 u , v , A i N 1 t 10 5 。保证
A i 两两不同。
lca倍增算法学习链接
321 最近公共祖先 倍增算法_哔哩哔哩_bilibili
LCA 板子
#include
using namespace std;
const int MAX=5e5+10;
vectore[MAX],w[MAX]; //用二维vector模拟邻接表存储树 
int fa[MAX][20]; //fa[u][j]是u向上跳2^j步到达的结点    最大20是因为该数据范围的树最多2^20层 
int deep[MAX];//存储每个结点的深度
int vis[MAX];
int N,M,S;
//求lca之前dfs一遍得到fa,deep表 ,dfs由上(father)向下(son)推,每次沿一条路径走到底 
void dfs(int u,int father)
{//father是u的父节点 
	if(vis[u])return;
	vis[u]=1;
	fa[u][0]=father;
	deep[u]=deep[father]+1;//更新深度
	for(int i=1;i<=19;i++)
	{//最多跳2^19步 
		fa[u][i]=fa[fa[u][i-1]][i-1]; //先跳一半到fa[u][i-1]再跳一半到u上面2^i 
	} 
	for(int v:e[u])
	{//往下在求u的所有孩子结点v 
		if(u!=v)dfs(v,u);
	}
	
}
/*返回u,v的最近公共祖先lca ,先把u,v跳到同一层,然后一起往上跳 */
int lca(int u,int v)
{
	if(deep[u]=0;i--)
	{//按二进制与十进制转化来跳 (注意要从大到小跳) 25=16+8+1 
		if(deep[fa[u][i]]>=deep[v])u=fa[u][i];	
	} 
	if(u==v)return u;//跳完后相等直接返回 
	for(int i=19;i>=0;i--)
	{//也是按二/十转化来挑 最后二者停在lca的下一层 
		if(fa[u][i]!=fa[v][i])
		{//注意不相等才跳 (因为跳大了可能不是最近的) 
			u=fa[u][i],v=fa[v][i];
		}
	}
	return fa[u][0];//因为上面停在lca下一层,所以返回u的上一层即为lca 
	
}
int main()
{
	cin>>N>>M>>S;
	for(int i=1;i>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
		w[u].push_back(v);
		w[v].push_back(u);
	}
	dfs(S,0);

	for(int i=0;i>u>>v;
		cout<
#include
using namespace std;
const int MAX=5e5+10;
vectore[MAX],w[MAX]; //用二维vector模拟邻接表存储树 
int fa[MAX][20]; //fa[u][j]是u向上跳2^j步到达的结点    最大20是因为该数据范围的树最多2^20层 
int deep[MAX];//存储每个结点的深度
long long dis[MAX]; //记录每个点到根节点的距离 
int A[MAX]; //存放游览路径 
int N,K;


//求lca之前dfs一遍得到fa,deep表 ,dfs由上(father)向下(son)推,每次沿一条路径走到底 
void dfs(int u,int father)
{//father是u的父节点 

	fa[u][0]=father;
	deep[u]=deep[father]+1;//更新深度
	for(int i=1;i<=19;i++)
	{//最多跳2^19步 
		fa[u][i]=fa[fa[u][i-1]][i-1]; //先跳一半到fa[u][i-1]再跳一半到u上面2^i 
	} 
	for(int i=0;i=0;i--)
	{//按二进制与十进制转化来跳 (注意要从大到小跳) 25=16+8+1 
		if(deep[fa[u][i]]>=deep[v])u=fa[u][i];	
	} 
	if(u==v)return u;//跳完后相等直接返回 
	for(int i=19;i>=0;i--)
	{//也是按二/十转化来挑 最后二者停在lca的下一层 
		if(fa[u][i]!=fa[v][i])
		{//注意不相等才跳 (因为跳大了可能不是最近的) 
			u=fa[u][i],v=fa[v][i];
		}
	}
	return fa[u][0];//因为上面停在lca下一层,所以返回u的上一层即为lca 
	
}
long long path_uv(int u,int v)
{//u到v距离为 u到根+v到根-2倍的lca到根 (画图看看) 
	if(!u||!v)return 0;//因为v->0应该为0 
	return dis[u]+dis[v]-2*dis[lca(u,v)];
}
int main()
{
	cin>>N>>K;
	for(int i=1;i>u>>v>>W;
		e[u].push_back(v);
		w[u].push_back(W);
		e[v].push_back(u);
		w[v].push_back(W);
	}
	
	dfs(1,0);
	long long sum=0;//储存未跳过的游览路径之和 
	for(int i=1;i<=K;i++)
	{
		cin>>A[i];
		sum+=path_uv(A[i-1],A[i]); 
	}
	for(int i=1;i<=K;i++)
	{//则却掉A[i]点路径之和为:总路径-A[i-1]到A[i] -A[i]到A[i+1] +A[i-1]到A[i+1],即从A[i-1]到A[i+1]不经过A[i] 
		cout<

试题 J: 砍树

【问题描述】
给定一棵由 n 个结点组成的树以及 m 个不重复的无序数对 ( a 1 , b 1 ) , ( a 2 , b 2 ) ,
. . . , ( a m , b m ) ,其中 a i 互不相同, b i 互不相同, a i , b j (1 i , j m )
小明想知道是否能够选择一条树上的边砍断,使得对于每个 ( a i , b i ) 满足 a i
b i 不连通,如果可以则输出应该断掉的边的编号(编号按输入顺序从 1
始),否则输出 -1
【输入格式】
输入共 n + m 行,第一行为两个正整数 n m
后面 n 1 行,每行两个正整数 x i y i 表示第 i 条边的两个端点。
后面 m 行,每行两个正整数 a i b i
【输出格式】
一行一个整数,表示答案,如有多个答案,输出编号最大的一个。
【样例输入】
6 2
1 2
2 3
4 3
2 5
6 5
3 6
4 5
【样例输出】
4
【样例说明】
断开第 2 条边后形成两个连通块: { 3 , 4 } { 1 , 2 , 5 , 6 } ,满足 3 6 不连通, 4
5 不连通。
断开第 4 条边后形成两个连通块: { 1 , 2 , 3 , 4 } { 5 , 6 } ,同样满足 3 6 不连
通, 4 5 不连通。
4 编号更大,因此答案为 4
【评测用例规模与约定】
对于 30 % 的数据,保证 1 < n 1000
对于 100 % 的数据,保证 1 < n 10^ 5 1 m 2 n
听说是树上差分+LCA,不会做
借用某大佬的
(34条消息) 2023第十四届蓝桥杯C/C++B组省赛题解_trudbot的博客-CSDN博客
#include 
using namespace std;
const int N = 1e5 + 10, M = 18;
vector> g[N];
int dep[N], f[N][20], cnt[N], w[N];

void init(int u, int fa) {
  dep[u] = dep[fa] + 1, f[u][0] = fa;
  for (int i = 1; (1 << i) <= dep[u]; i ++) f[u][i] = f[f[u][i - 1]][i - 1];
  for (auto &e : g[u]) {
    if (e.first != fa) init(e.first, u), w[e.first] = e.second;
  }
}

int lca(int a, int b) {
  if (dep[a] < dep[b]) swap(a, b);
  for (int i = M; i >= 0; i --) {
    if (dep[f[a][i]] >= dep[b]) a = f[a][i];
    if (a == b) return a;
  }
  for (int i = M; i >= 0; i --) {
    if (f[a][i] != f[b][i]) a = f[a][i], b = f[b][i];
  }
  return f[a][0];
}

void add(int a, int b) {
  int LCA = lca(a, b);
  cnt[a] ++, cnt[b] ++, cnt[LCA] -= 2;
}

void dfs(int u, int fa) {
  for (auto &e : g[u]) {
    if (e.first != fa) 
      dfs(e.first, u), cnt[u] += cnt[e.first];
  }
}

int main () {
  int n, m; cin >> n >> m;
  for (int i = 1; i < n; i ++) {
    int a, b; cin >> a >> b;
    g[a].push_back({b, i}), g[b].push_back({a, i});
  }
  init(1, 0);
  for (int i = 0; i < m; i ++) {
    int a, b; cin >> a >> b;
    add(a, b);
  }
  dfs(1, 0);
  int res = -1;
  for (int i = 1; i <= n; i ++) 
    if (cnt[i] == m && (w[i] > res)) res = w[i];
  cout << res << endl;
  return 0;
}

你可能感兴趣的:(蓝桥杯,c++,算法)