蓝桥杯C++大学B组一个月冲刺记录2024/3/15
规则:每日三题
好朋友说经常吃(#`O′)维C的话,需要多喝水不然会得结石喔
1.树的重心
给定一颗树,树中包含 n个结点(编号 1∼n)和 n−1条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
这人图是无向连通图 == 是个树
这个题的关键思想定义好dfs求的东西:
这个dfs的定义是:(从1号点出发后)将u看作根节点时的子树大小(连通块大小)。并且dfs后能够把u各子节点所在的连通块大小求出来。
(我拿到这个题:直接想的是从各点出发,寻找每一个点的所有子节点所在的连通块大小。时间复杂度是O(m * n2),会tle)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int M = 1e5 + 10;
int h[2 * M],ne[2 * M],e[2 * M],idx;
bool st[M];
void add(int a,int b){
e[idx] = b; ne[idx] = h[a]; h[a] = idx; idx++;
}
int dfs(int u){
st[u] = true;
int tot = 1;
for(int i = h[u];i != -1;i = ne[i]){
int j = e[i];
if(!st[j]){
tot += dfs(j);
}
}
return tot;
}
int check(int u){
int ans = 0;
memset(st,0,sizeof(st));
st[u] = true;
for(int i = h[u];i != -1;i = ne[i]){
if(!st[e[i]]) ans = max(ans,dfs(e[i]));
}
return ans;
}
int main(){
int n;
cin >> n;
int res = n;
memset(h,-1,sizeof(h));
for(int i = 1;i<=n;++i){
int a,b;
cin >> a >> b;
add(a,b),add(b,a);
}
for(int i = 1;i <= n; ++i){
res = min(res,check(i));
}
cout << res << endl;
return 0;
}
2.大臣的旅费
很久以前,T王国空前繁荣。
为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。
为节省经费,T 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。
同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
J是 T 国重要大臣,他巡查于各大城市之间,体察民情。
所以,从一个城市马不停蹄地到另一个城市成了 J 最常做的事情。
他有一个钱袋,用于存放往来城市间的路费。
聪明的 J 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。
具体来说,一段连续的旅途里,第 1 千米的花费为 11,第 2 千米的花费为 12,第 3 千米的花费为 13,…,第 x
千米的花费为 x+10。
也就是说,如果一段旅途的总长度为 1千米,则刚好需要花费 11,如果一段旅途的总长度为 2 千米,则第 1 千米花费 11
,第 2千米花费 12,一共需要花费 11+12=23。
J 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
同样:无向连通图 == 树
简而言之:每个题就是求树的直径,即树中两个点的距离最大值。
树的直径求法:
(1)从任取的A点出发,遍历整棵树,求得所有点到A的距离。找到离A点距离最远的点B
(2)从B点出发,再一次遍历整棵树,求得所有点到B的距离,找到离B点距离最远的点C
(3)B点到C点的距离就是树的直径
(证明略)
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int h[N],e[N * 2],ne[N * 2],w[N * 2],idx;
bool st[N];
int dist[N];
typedef long long LL;
void add(int a,int b,int c){
e[idx] = b,ne[idx] = h[a],h[a] = idx,w[idx] = c,idx++;
}
int n,ans = 0;
void dfs(int u){
st[u] = true;
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(st[j]) continue;
else{
dist[j] = dist[u] + w[i];
dfs(j);
}
}
return;
}
int main(){
memset(h,-1,sizeof(h));
cin >> n;
for(int i = 1;i <= n;++i){
int a,b,c;
cin >> a >> b >> c;
add(a,b,c),add(b,a,c);
}
dfs(1);
int mx = 0,k;
for(int i = 1;i<=n;++i){
if(dist[i] > mx){
mx = dist[i];
k = i;
}
}
memset(st,0,sizeof(st));
dist[k] = 0;
dfs(k);
for(int i = 1;i <= n;++i)
if(dist[i] > ans) ans = dist[i];
LL res = (LL)ans * 10 + (LL)(ans + 1) * (ans) / 2;
cout << res << endl;
return 0;
}
3.扫雷
小明最近迷上了一款名为《扫雷》的游戏。
其中有一个关卡的任务如下:
在一个二维平面上放置着 n个炸雷,第 i 个炸雷 (xi,yi,ri)。表示在坐标 (xi,yi) 处存在一个炸雷,它的爆炸范围是以半径为 ri 的一个圆。
为了顺利通过这片土地,需要玩家进行排雷。
玩家可以发射 m 个 排雷火箭,小明已经规划好了每个排雷火箭的发射方向,第 j 个排雷火箭 (xj,yj,rj) 表示这个排雷火箭将会在 (xj,yj) 处爆炸,它的爆炸范围是以半径为 rj 的一个圆,在其爆炸范围内的炸雷会被引爆。
同时,当炸雷被引爆时,在其爆炸范围内的炸雷也会被引爆。
现在小明想知道他这次共引爆了几颗炸雷?
你可以把炸雷和排雷火箭都视为平面上的一个点。
一个点处可以存在多个炸雷和排雷火箭。
当炸雷位于爆炸范围的边界上时也会被引爆。
(这个题真的难过,有无数的牢骚想发)
这个题的关键思想:
(1)如果使用dfs和bfs,涉及到每一次遍历需要遍历每一个炸弹时间复杂度是O(n2)
所以可以将 ( x , y )转化为long long,然后能想到通过哈希映射时间复杂度O(n)。
(2)需要观察:r的范围非常小,所以可以穷举圆内所有的符合条件的点。
(3)将 ( x , y )转化为long long:将xy看成1e9进制的数。所以特征值变成了
x * 1000000001ll + y(1000000001ll会自动把1000000001转化成longlong)
#include<iostream>
#include<cstring>
using namespace std;
const int M = 50010;
const int N = 999997;
typedef long long LL;
struct Cir{
int x,y,r;
}q[M];
LL h[N];
int id[N];
bool st[N];
int n,m,ans = 0;
LL query_code(int x,int y)
{
return x * 1000000001ll + y;
}
int find(LL x){
int k = (x % N + N) % N;
while(h[k] != -1&&h[k] != x){
k++;
if(k >= N) k = 0;
}
return k;
}
int sqr(int x){
return x * x;
}
void dfs(int x,int y,int r){
LL code = query_code(x,y);
int k = find(code);
st[k] = true;
for(int i = x - r;i <= x + r; ++i){
for(int j = y - r;j <= y + r; ++j){
if(sqr(i - x) + sqr(j - y) <= sqr(r))
{
code = query_code(i,j);
k = find(code);
if(h[k] == code && !st[k]) dfs(i,j,q[id[k]].r);
}
}
}
return;
}
int main(){
memset(h,-1,sizeof(h));
cin >> n >> m;
int x,y,r;
for(int i = 1;i <= n; ++i){
cin >> x >> y >> r;
q[i] = {x,y,r};
LL code = query_code(x,y);
int k = find(code);
if(h[k] == -1) h[k] = code;
if(!id[k] || q[id[k]].r < r) id[k] = i;
}
for(int i = 1;i <= m; ++i){
cin >> x >> y >> r;
for(int i = x - r;i <= x + r; ++i)
for(int j = y - r;j <= y + r; ++j)
if(sqr(i - x) + sqr(j - y) <= sqr(r))
{
LL code = query_code(i,j);
int k = find(code);
if(h[k] == code && !st[k]) dfs(i,j,q[id[k]].r);
}
}
ans = 0;
for(int i = 1;i <= n;++i){
LL code = query_code(q[i].x,q[i].y);
int k = find(code);
if(st[k]) ans ++;
}
cout << ans << endl;
return 0;
}