蓝桥杯C++大学B组一个月冲刺记录2024/3/15

蓝桥杯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;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值