nefu暑假集训3 并查集与最小生成树 个人模板+例题汇总

news2024/11/13 10:49:01

前言:

  并查集与最小生成树的训练。

正文:

链接:并查集与最小生成树 - Virtual Judge (vjudge.net)

题目:

A - 关押罪犯:

#include <bits/stdc++.h>
using namespace std;
const int N=200005;
int fa[N],d[N];
typedef struct stu{
	int a;
	int b;
	int c;
}node; 
node q[N];
int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	x=find(x);y=find(y);
	fa[x]=y;
}
bool cmp(node x,node y){
	return x.c>y.c;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		cin>>q[i].a>>q[i].b>>q[i].c;
	}
	sort(q+1,q+m+1,cmp);
	for(int i=1;i<=m+1;i++){
		if(find(q[i].a)==find(q[i].b)){
			cout<<q[i].c;
			break;
		}
		else{
			if(!d[q[i].a])d[q[i].a]=q[i].b;
			else merge(d[q[i].a],q[i].b);
			if(!d[q[i].b])d[q[i].b]=q[i].a;
			else merge(d[q[i].b],q[i].a);
		}
	}
	return 0;
}

我们先将所有囚犯之间的矛盾按怨气大小排序,怨气最大的那两个人一定是优先放在俩个地方,我们枚举排序过的囚犯组合,先判断他们是否再同一并查集内,若是则直接输出答案,不是就分别判断他俩目前是否有敌人,若无则将敌人记录下来,若有则将该囚犯的两个敌人合并至一个并查集内(敌人的敌人就是朋友),不断进行以上操作最终可以得到答案。

B - Shichikuji and Power Grid:

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
typedef pair<int,int> PII;
struct node{
	int x,y,c,k;
}a[2005];
struct nod{
	int fa,ne,w; 
}p[4000040];
int cnt = 0;
int sum = 0;
vector<int> ans;
vector<PII> res; 
int f[2005];
int find(int x){
	if(f[x] == x)
	return x;
	return f[x] = find(f[x]); 
}
bool cmp(nod a,nod b){
	return a.w < b.w;
} 
void solve(){
	int n;
	cin >> n; 
	for(int i = 1;i <= n;i++){
		cin >> a[i].x >> a[i].y;
	}
	for(int i = 1;i <= n;i++)
	f[i] = i;
	for(int i = 1;i <= n;i++){
		cin >> a[i].c;
		p[++cnt].fa = 0;
		p[cnt].ne = i;
		p[cnt].w = a[i].c;
	}
	for(int i = 1;i <= n;i++){
		cin >> a[i].k;
	}
	for(int i = 1;i <= n;i++){
		for(int j = i + 1;j <= n;j++){
			p[++cnt].fa = i;
			p[cnt].ne = j;
			p[cnt].w = (abs(a[i].x - a[j].x) + abs(a[i].y - a[j].y))*(a[i].k + a[j].k);
		}
	}
	sort(p + 1,p +1 + cnt,cmp);
	int s = 0;
	for(int i = 1;i <= cnt;i++){
		int x = find(p[i].fa);
		int y = find(p[i].ne);
		if(x == y){
			continue;
		}
		if(p[i].fa == 0){
			ans.push_back(p[i].ne);
		}
		else{
			res.push_back({p[i].fa,p[i].ne});
		}
		f[x] = y;
		sum += p[i].w;
		s++; 
		if(s == n)
		break;
	}
	cout << sum <<"\n";
	cout << ans.size() <<"\n";
	for(auto i:ans)	
	cout <<i << ' ';
	cout <<"\n";
	cout << res.size() <<"\n";
	for(auto t:res){
		cout <<t.first <<" "<<t.second <<"\n";
	}
}
signed main(){
	int t = 1;
	while(t--)
	{
		solve(); 
	}
	return 0;
}

超级源点问题。

C - Power Tree:(待补)

 

D - 食物链:

 

#include <bits/stdc++.h>
using namespace std;
const int N = 50010;
int n, m;
int p[N], d[N];
int find(int x){
    if (p[x] != x){
        int t = find(p[x]);
        d[x] += d[p[x]];
        p[x] = t;
    }
    return p[x];
}
int main(){
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) p[i] = i;
	int res = 0;
    while (m -- ){
        int t, x, y;
        scanf("%d%d%d", &t, &x, &y);
        if (x > n || y > n) res ++ ;
        else{
            int px = find(x), py = find(y);
            if (t == 1){
                if (px == py && (d[x] - d[y]) % 3) res ++ ;
                else if (px != py){
                    p[px] = py;
                    d[px] = d[y] - d[x];
                }
            }
            else{
                if (px == py && (d[x] - d[y] - 1) % 3) res ++ ;
                else if (px != py){
                    p[px] = py;
                    d[px] = d[y] + 1 - d[x];
                }
            }
        }
    }
    printf("%d\n", res);
    return 0;
}

非常经典的带权并查集题目了,我们可以开一个数组d,d[i]表示i这个点到根节点的距离,我们我们将同类的点距离设为0,捕食关系距离设为1,被捕食关系距离设为2,我们就可以据此通过两个点之间的距离%3来判断他们之间的关系。

E - How Many Answers Are Wrong:(待补)

 

F - 银河英雄传说:

 

#include <bits/stdc++.h>
using namespace std;
const int N=300005;
int fa[N],d[N],a[N];
int find(int x){
	if(fa[x]!=x){
		int u=fa[x];
		fa[x]=find(fa[x]);
		d[x]+=d[u];
		a[x]=a[fa[x]];
	}
	return fa[x];
}
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=300000;i++){
		fa[i]=i;d[i]=0;a[i]=1;
	}
	for(int i=1;i<=n;i++){
		char s;
		int x,y,fx,fy;
		cin>>s>>x>>y;
		if(s=='M'){
			fx=find(x);fy=find(y);
			fa[fx]=fy;
			d[fx]+=a[fy];
			a[fx]+=a[fy];a[fy]=a[fx];
		}
		if(s=='C'){
			fx=find(x);fy=find(y);
			if(fx!=fy){
				cout<<-1<<endl;
				continue;
			}
			else{
				cout<<abs(d[x]-d[y])-1<<endl;
			}
		}
	}
	return 0;
}

这题和上题类似,不过多了一个a数组,主要作用是维护连通块的大小以正确表达出该点到根节点的距离。

G - News Distribution:

 

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int maxn=1e6+5;
int n,m,f[maxn],num[maxn];
int find_x(int x) {
	if(f[x]!=x)return f[x]=find_x(f[x]);
	else return f[x];
}
void unite(int x,int y) {
	int aa=find_x(x);
	int bb=find_x(y);
	if(aa!=bb)f[aa]=bb;
}
int main() {
	IOS;
	cin>>n>>m;
	for(int i=0; i<=n; i++)f[i]=i;
	while(m--){
		int k,a,b;
		cin>>k;
		if(k!=0)cin>>a;
		for(int i=1; i<k; i++) {
			cin>>b;
			unite(a,b);
		}
	}
	for(int i=1; i<=n; i++)num[find_x(i)]++;
	cout<<num[find_x(1)];
	for(int i=2; i<=n; i++)cout<<" "<<num[find_x(i)];
	cout<<endl;
}
 

题目可理解为输出1 - n每个号码所在团体总人数,利用并查集不断向集合添加成员,记录每个集合的人数,保存在根节点的sum[]中,查询每个节点的根节点,输出根节点sum

H - Phase Shift(待补):

 

I - 星球大战:​​​​​​​

#include <bits/stdc++.h>
using namespace std;
const int N = 4 * 1e5 + 10; // 全局变量;
int res;    // 储存答案;
int n, m, k;
// n 星球数目, m 以太隧道数目, k 遭受攻击的星球数目
int fa[N];
// 并查集
int h[N], s[N], e[N], ne[N], idx;
// 存图, e[] 存边到达的终点, s 存边的起点
bool st[N];

void add(int a, int b) 
{
    e[idx] = b, s[idx] = a, ne[idx] = h[a], h[a] = idx ++;
}

int find(int x) // 并查集, 找祖先 (路径压缩)
{
    if (x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    memset(h, -1, sizeof h);    // 初始化表头
    memset(st, true, sizeof st);// 初始时没有点被摧毁

    scanf("%d %d", &n, &m);

    for (int i = 1; i <= n; i ++ ) fa[i] = i;

    for (int i = 1; i <= m; i ++ )  
    {
        int x, y;

        scanf("%d %d", &x, &y);
        add(x, y), add(y, x);   // 读入无向边
    }

    scanf("%d", &k);    // 读入遭受攻击的星球数目

    stack<int> pk, ans; 
    // 分别储存 pk 被攻击的星球, ans 输出答案
    for (int i = 1; i <= k; i ++ )
    {
        int q;
        scanf("%d", &q);

        st[q] = false, pk.push(q);  
        // 标记为摧毁, 算法核心是将摧毁转换成 修建 , 所以逆时间顺序压入栈中
    }

    m = 2 * m, res = n - k;
    // 无向边边数 * 2, res 储存连通块数量
    for (int i = 1; i <= m; i ++ )  // 先找所有完好的边构成的连通块数量
    {
        int S = find(s[i]), E = find(e[i]); 
        // 先找到这条边的入点和出点
        if(st[e[i]] && st[s[i]] && S != E)  // 如果这两条边都没有被摧毁, 且不在同一个连通块中
        {
            res --;     // 连通块的数量就可以减一了
            fa[E] = S;  // 加入同一个连通块
        }
    }

    ans.push(res);  // 逆时间的初始答案, k 个点都被摧毁 或 理解为没有新增的点
    while(pk.size())    // 只要不为空
    {
        int tot = pk.top(); // 取出栈顶
        pk.pop();
        res ++;         // 多了一个星球,连通块数量加一
        st[tot] = true; // 当前星球已被修建

        for (int i = h[tot]; i != -1; i = ne[i])    // 遍历这个点的所有出边
        {
            int j = e[i];   // 边的终点
            int S = find(tot), E = find(e[i]);  // 找到起点和终点的祖先
            if (st[j] && S != E)    // 终点没有被摧毁, 不是一个祖先也就是不在一个连通块中
            {
                res --;     // 连通块数量减一
                fa[E] = S;  // 加入一个连通块
            }
        }
        ans.push(res); // 压入当前修建好这个星球的答案
    }
    while (ans.size()) // 逆输出答案
    {
        res = ans.top();
        ans.pop();
        printf("%d\n", res);
    }

    return 0;
}

 逆向过程建图。

J - 货车运输:​​​​​​​

 

#include<bits/stdc++.h>  
const int  MAXN =10005 ;
const long long  INF =999999999; 
using namespace std; 
struct Edge1{  
    int x,y,dis;
}edge1[50005]; 
struct Edge2{
    int to,next,w;
}edge2[100005];  
int cnt,n,m,head[MAXN],deep[MAXN],f[MAXN],fa[MAXN][21],w[MAXN][21];
bool vis[MAXN]; 
void addedge(int from, int to, int w){
    edge2[++cnt].next=head[from];
    edge2[cnt].to=to;
    edge2[cnt].w=w;
    head[from]=cnt;
    return ;
}

bool CMP(Edge1 x, Edge1 y){
    return x.dis>y.dis;
}

int find(int x){ 
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}

void kruskal(){
    sort(edge1+1, edge1+m+1, CMP); 
    for(int i=1; i<=n; i++)
        f[i]=i;  
    for(int i=1; i<=m; i++)
        if(find(edge1[i].x)!=find(edge1[i].y)){
            f[find(edge1[i].x)]=find(edge1[i].y);
            addedge(edge1[i].x, edge1[i].y, edge1[i].dis);
            addedge(edge1[i].y, edge1[i].x, edge1[i].dis); 
			}
    return ;
}

void dfs(int node){
    vis[node]=true;
    for(int i=head[node]; i; i=edge2[i].next){ 
        int to=edge2[i].to;
        if(vis[to]) continue;
        deep[to]=deep[node]+1; 
        fa[to][0]=node; 
        w[to][0]=edge2[i].w;  
        dfs(to);
    }
    return ;
}

int lca(int x, int y)
{
    if(find(x)!=find(y)) return -1; 
    int ans=INF;
    if(deep[x]>deep[y]) swap(x,y);  
    for(int i=20; i>=0; i--)
        if(deep[fa[y][i]]>=deep[x]){
            ans=min(ans, w[y][i]); 
            y=fa[y][i]; 
        }
    if(x==y) return ans;
  
    for(int i=20; i>=0; i--)
        if(fa[x][i]!=fa[y][i]){
            ans=min(ans, min(w[x][i], w[y][i]));
            x=fa[x][i]; 
            y=fa[y][i];
        }
    ans=min(ans, min(w[x][0], w[y][0]));
    return ans;
}

int main()
{
    int x,y,z,q;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++){
        scanf("%d%d%d",&x,&y,&z);
        edge1[i].x=x;
        edge1[i].y=y;
        edge1[i].dis=z;
    }
    kruskal();
    for(int i=1; i<=n; i++)
        if(!vis[i]){
            deep[i]=1; 
            dfs(i);
            fa[i][0]=i;
            w[i][0]=INF;
        }
    for(int i=1; i<=20; i++)
        for(int j=1; j<=n; j++){
            fa[j][i]=fa[fa[j][i-1]][i-1]; 
            w[j][i]=min(w[j][i-1], w[fa[j][i-1]][i-1]);
        }
    scanf("%d",&q);
    for(int i=1; i<=q; i++){
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y)); 
    }
    return 0;
} 

最大生成树+lca倍增。

K - 程序自动分析:​​​​​​​

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int N = 200010;

int n, m;
int p[N];
unordered_map<int, int> S;

struct Query
{
    int x, y, e;
}query[N];

int get(int x)
{
    if (S.count(x) == 0) S[x] = ++ n;
    return S[x];
}

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        n = 0;
        S.clear();
        scanf("%d", &m);
        for (int i = 0; i < m; i ++ )
        {
            int x, y, e;
            scanf("%d%d%d", &x, &y, &e);
            query[i] = {get(x), get(y), e};
        }

        for (int i = 1; i <= n; i ++ ) p[i] = i;

        // 合并所有相等约束条件
        for (int i = 0; i < m; i ++ )
            if (query[i].e == 1)
            {
                int pa = find(query[i].x), pb = find(query[i].y);
                p[pa] = pb;
            }

        // 检查所有不等条件
        bool has_conflict = false;
        for (int i = 0; i < m; i ++ )
            if (query[i].e == 0)
            {
                int pa = find(query[i].x), pb = find(query[i].y);
                if (pa == pb)
                {
                    has_conflict = true;
                    break;
                }
            }

        if (has_conflict) puts("NO");
        else puts("YES");
    }

    return 0;
}

见注释。

L - 蔬菜:(待补)

 这个真不会捏。

后记:

  里面有些题难度已经很高了,我连题解都看不懂,以后再补吧!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2092813.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实现一个能设置MaxLine的LayoutManager

实现一个能设置MaxLine的LayoutManager 有时候&#xff0c;我们会遇到这种需求&#xff1a;一个线性的列表布局&#xff0c;当item量很少的时候&#xff0c;就是wrap_content直接展示完所有item&#xff0c;但是当item数量超过某个数时就要固定高度&#xff0c;让其变成可滑动…

AOP 面向切片编程

目录 1. 什么是AOP 2. AOP的应用场景 3. AOP在Java中的实现 4. Spring AOP概述 5. Spring AOP的配置 1.在pom.xml文件中添加Spring AOP的依赖&#xff1a; 2. 定义切面 3. 启用AOP 4. 目标类 5. 测试AOP 6. AOP与其他技术的对比 面向切面编程&#xff08;AOP, Aspec…

SPI通信(软件模拟)

1 软件SPI程序框架 2 软件SPI交换一个字节的先后顺序 3 读取W25q64芯片厂商ID,设备ID 4 宏定义W26q64指令码 5 页缓冲区最大256字节&#xff0c;超过就会忙碌 6 页编程

生产环境中变态开启devtools(强制)

写到最前面 首先&#xff0c;你已经下载了google的插件【vue devtools】&#xff0c;不知道怎么下载&#xff0c;留言博主 如果你想看的项目中的vuetools插件打开是这样的 Vue.js is detected on this page. Devtools inspection is not available because it’s in product…

【中学教资-信息技术】多媒体文件大小的存储

图像/音频/视频文件大小的计算 1 图像文件2 音频文件3 视频文件4 例题5 总结 视频讲解&#xff1a;音频文件大小/视频文件大小计算-失舵之舟 1 图像文件 压缩比原始大小/被压缩之后大小 颜色深度&#xff1a;指图像中每个像素所占的二进制位数&#xff08;bit&#xff09; n位…

BioXM一款实用的生物小软件

软件介绍 BioXM是一款非常简单明了的分子生物学软件&#xff0c;全中文操作界面非常友好。 DNA序列分析、蛋白质序列分析、多序列比对、氨基酸相似性计算、酶切位点分析、ORF分析、分子量和等电点预测、序列数据库等。 软件下载 https://pan.quark.cn/s/84df485efd6e安装教…

Vxe UI vue vxe-table 如何在表格中使用上传附件、上传图片

Vxe UI vue vxe-table 如何在表格中使用上传附件、上传图片 效果图 在表格中显示缩略图模式上传附件或图片 点击更多按钮查看全部 鼠标拖动上传 代码 <template><div><vxe-grid v-bind"gridOptions"></vxe-grid></div> </tem…

第九周:机器学习

目录 摘要 Abstract 一、RNN 1、引入 2、RNN的分类 二、LSTM 1、基本结构 2、具体步骤 3、举例说明 4、原理理解 总结 摘要 本周主要围绕RNN进行探讨&#xff0c;从为什么需要这类”循环网络“入手&#xff0c;提到了”slot filling“技术&#xff0c;接着又对R…

大模型赋能风控运营:效率跃升的密码

一、大模型助力风控运营的背景与趋势 大模型兴起的背景 随着金融行业的迅速发展和数据量的爆炸式增长&#xff0c;传统的风控运营手段逐渐难以满足复杂多变的风险形势。大数据、人工智能等技术的不断进步&#xff0c;为大模型在风控运营领域的应用提供了技术支撑。金融机构面…

海洋大地测量基准与水下导航系列之一引子

海底大地基准网是一组布放在海底的声学基准站&#xff0c;组建类似GNSS星座的定位系统&#xff0c;既可以对水面及水下的各类设备提供时间和空间信息&#xff0c;也可用来监测海底板块及水体环境的动态变化。海底声学基准站与海面GNSS、海底重力站联合打造立体的观测控制网&…

十道MySQL必问面试题

你是如何理解最左前缀原则的&#xff1f;你是如何理解行锁、GAP锁、临键锁的&#xff1f;你是如何理解MVCC的&#xff1f;你是如何理解count(*)和count(1)的&#xff1f;你是如何理解Online DDL的&#xff1f;你知道哪些情况下会导致索引失效&#xff1f;你是如何理解filesort的…

fastmock使用

FastMock 是一个在线工具&#xff0c;用于快速创建和管理模拟 API&#xff08;Mock API&#xff09;。它主要用于前端开发&#xff0c;允许开发者在没有真实后端服务的情况下&#xff0c;模拟 API 响应&#xff0c;从而加速开发和测试过程。 FastMock网址&#xff1a;fastmock.…

Java多进程调用dll程序和exe程序

&#x1f3af;导读&#xff1a;本文介绍了使用Java调用本地DLL及EXE程序的方法。针对DLL调用&#xff0c;文章提供了基于Java Native Access (JNA) 库的具体实现方案&#xff0c;包括定义Java接口以映射DLL中的函数&#xff0c;并展示了如何加载DLL及调用其中的方法。对于EXE程…

Python 数据可视化:工具与实践

文章目录 数据可视化可视化库特点对比实例&#xff1a;绘制基本数据分布图评估维度 交互式可视化与静态图表实例&#xff1a;创建交互式折线图评估维度 实时数据可视化实例&#xff1a;展示实时股票数据评估维度 图表设计原则实例&#xff1a;设计适合展示销售数据的条形图评估…

论文辅助笔记:LP_BERT

1 train_task1.py 1.1 main部分 读取命令行参数&#xff0c;调用task1函数 1.2 task1 train 1.3 task1 valid 1.3 collate_fn 2 Dataset 2.1 train dataset 2.2 valid dataset 3 LPBERT 3.1 不同的embedding day-of-week embedding和time-of-day embedding X位置和Y位置的…

色彩与笔触的交响:广州米塔在线科教技术有限公司揭秘PS绘画秘籍!

在数字艺术的广阔天地里,PS无疑是一颗璀璨的明星&#xff0c;它不仅在图像处理领域独领风骚&#xff0c;更以其强大的功能成为了众多艺术家和设计师进行数字绘画的首选工具。广州米塔在线科教技术有限公司&#xff0c;作为致力于艺术教育与技术分享的平台&#xff0c;深知掌握P…

sealos快速搭建k8s集群

一&#xff0c;环境准备 1&#xff0c;三台&#xff08;搭建一主两从集群&#xff09;或五台&#xff08;三主两从集群&#xff09;虚拟机&#xff0c; 安装alimaLinux系统 &#xff0c;相同的root密码&#xff0c;不要安装docker。 如果是alimaLinux-mini版本操作系统&#xf…

PMP–知识卡片--SCQA金字塔表达

SCQA模型通过四个关键元素&#xff1a;情景冲突疑问答案&#xff0c;建立了一个精确而有逻辑的表达框架。同时&#xff0c;它也能够帮助你构建合理的逻辑链条&#xff0c;让别人更容易理解和接受你的观点。 情景&#xff1a;通过描述背景和现状引入话题&#xff0c;这个元素帮助…

两个月冲刺软考——关系模式中的候选关键字与如何分解为无损连接并保持函数依赖的解法(例题讲解,看完必会)

1. 数据库中的简单属性、多值属性、复合属性、派生属性 简单属性&#xff1a;指不能够再分解成更小部分的属性&#xff0c;通常是数据表中的一个列。例如学生表中的“学号”、“姓名”等均为简单属性。 多值属性&#xff1a;指一个属性可以有多个值。例如一个学生可能会有多个…

掌握EF Core:全方位面试指南,助你从初级到高级轻松晋级

一、前言 这份指南旨在帮助你为主要考察 Entity Framework Core (EF Core) 的面试做好准备&#xff0c;内容涵盖基础、中级和高级三个不同经验级别。每个级别包括10个高频面试题&#xff0c;附有解题思路和详细的解答示例。 二、基础级别 重点在于 EF Core 的基本概念和使用…