首页 > 其他分享 >【题解】Atcoder ABC302 F,G,Ex

【题解】Atcoder ABC302 F,G,Ex

时间:2023-05-21 16:44:09浏览次数:33  
标签:nxt val int 题解 top ABC302 flag Ex now

完全不会 G 和 Ex,这些套路还是要积累一下的。

F.Merge Set

题目描述:

给定 \(n\) 个集合,每次可以合并两个有交的集合,问最少多少次合并可以让 \(1\) \(m\) 位于同一个集合中。

题目分析:

一开始将题读成了将 \([1,m]\) 位于同一个集合中,然后就感觉这个题相当不可做。
因为集合的元素之间没有任何关系,而如果直接考虑集合之间怎么弄弄的话肯定是不行的,而题目要求每次合并两个有交的集合,那么不妨转化为图论。
也就是将所有有交的集合连在一起,但是这样的复杂度就炸天了,而且也没法统计答案。
所以就考虑对于每一个数建一个点,然后对于每一个集合向它包含的数连边,这样的话就完美解决了上述问题。
这样其实就直接跑一个从数 \(1\) 到数 \(m\) 的最短路然后稍微处理一下就是答案。
因为最小情况下我们每次合并一个区间,肯定都是要用这个区间里新加入的数扩展新的区间,所以直接求没问题。

代码:

点击查看代码
#include<bits/stdc++.h>
#define PII pair<int,int>
using namespace std;
const int N = 2e6+5;
struct edge{
	int nxt,to,val;
	edge(){}
	edge(int _nxt,int _to,int _val){
		nxt = _nxt,to = _to,val = _val;
	}
}e[2 * N];
int head[N],dis[N],cnt;
bool vis[N];
void add_edge(int from,int to,int val){
	e[++cnt] = edge(head[from],to,val);
	head[from] = cnt;
}
int main(){
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++){
		int a;scanf("%d",&a);
		for(int j=1; j<=a; j++){
			int k;scanf("%d",&k);
			add_edge(k,m+i,1);
			add_edge(m+i,k,1);
		}
	}
	memset(dis,0x3f,sizeof(dis));
	priority_queue<PII> q;q.push({0,1});
	dis[1] = 0;
	while(!q.empty()){
		int now = q.top().second;q.pop();
		if(vis[now])	continue;
		vis[now] = true;
		for(int i=head[now]; i; i=e[i].nxt){
			int to = e[i].to;
			if(dis[to] > dis[now] + e[i].val){
				dis[to] = dis[now] + e[i].val;
				q.push({-dis[to],to});
			}
		}
	}
	int ans = dis[m] / 2 - 1;
	if(ans > 10000000)	printf("%d\n",-1);
	else	printf("%d\n",ans);
	return 0;
}

G.Sort from 1 to 4

题目描述:

给定长度为 \(n\) 的仅包含 \([1,4]\) 的序列,每次可以交换两个数,问最少多少次交换后序列单调不降。

题目分析:

考虑将序列排序肯定就是形如 111...222...333...444... 所以显然可以对于每一个位置的数找到其对应换成的数,若数 \(i\) 要变为数 \(j\) 则记为 \((i,j)\)。
如果存在 \((i,j)\) 也存在 \((j,i)\) 那么直接这两个换就好了,考虑扩展其实就是:如果要成功换到肯定是这些置换构成一个环,所以直接对于二元环、三元环、四元环都统计一下就好了,注意要按照这种顺序统计,因为这样能保证答案尽可能小。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6+5;
const int INF = 1e9+5;
int ans = 0,lm,a[N],b[N],cnt[10][10];
bool vis[N];
void dfs(int now){
	if(now == lm+1){
		int tmp = INF;
		for(int i=1; i<lm; i++)	tmp = min(tmp,cnt[a[i]][a[i+1]]);
		tmp = min(tmp,cnt[a[lm]][a[1]]);
		ans += tmp * (lm - 1);
		for(int i=1; i<lm; i++)	cnt[a[i]][a[i+1]] -= tmp;
		cnt[a[lm]][a[1]] -= tmp;
		return;
	}
	for(int i=1; i<=4; i++){
		if(vis[i])	continue;
		a[now] = i;vis[i] = true;
		dfs(now+1);
		vis[i] = false;
	}
}
int main(){
	int n;scanf("%d",&n);
	for(int i=1; i<=n; i++)	scanf("%d",&a[i]),b[i] = a[i];
	sort(b+1,b+n+1);
	for(int i=1; i<=n; i++)	cnt[a[i]][b[i]]++;
	lm = 2;dfs(1);
	lm = 3;dfs(1);
	lm = 4;dfs(1);
	printf("%d\n",ans);
	return 0;
}

Ex.Ball Collector

题目分析:

给定一棵 \(n\) 个点的树,每个点有两个小球,权值分别为 \(a_i,b_i\),经过这个点就可以选择两个小球中的一个。对于 \(v \ge 2\),都要求出 \(1\) 到 \(v\) 路径上选出来的小球中不同整数个数的最大值。

题目分析:

两个数只能选一个,这就很图论。
也就是若路径中存在点 \(i\) 则建边:\((a_i,b_i)\)。
这样就能发现对于每一个连通块,若为树则贡献为点数-1,否则贡献为点数。
因为我们要高效维护这个信息,并且我们只关注连通性问题,所以可以考虑直接使用并查集维护。
那么我们可以直接 dfs 这棵树,然后每一次回溯都是相当于撤销最后一次操作,用可撤销并查集就可以了。
可撤销并查集不可路径压缩

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
struct node{
	int x,y,fa,fx,fy,val,sz;
}st[N];
struct edge{
	int nxt,to;
	edge(){}
	edge(int _nxt,int _to){
		nxt = _nxt,to = _to;
	}
}e[2*N];
int cnt,top,tmp,ans[N],head[N],fa[N],flag[N],sz[N],a[N],b[N],tot[N];
void add_edge(int from,int to){
	e[++cnt] = edge(head[from],to);
	head[from] = cnt;
}
int find(int x){
	if(fa[x] == x)	return x;
	return find(fa[x]);
}
void merge(int x,int y){
	x = find(x),y = find(y);
	if(sz[x] > sz[y])	swap(x,y);
	if(x == y){
		st[++top]  = {x,x,fa[x],flag[x],flag[x],flag[x],0};
		tmp += flag[x];
		flag[x] = false; 
		return;
	}
	int val = 0;
	if(flag[x] && flag[y])	val = 1;
	else	val = flag[x] ^ flag[y];
	st[++top] = {x,y,fa[x],flag[x],flag[y],val,sz[x]};
	tmp += val; sz[y] += sz[x];
	fa[x] = y;
	if(!flag[x])	flag[y] = false;
}
void del(){
	int x = st[top].x,y = st[top].y;
	tmp -= st[top].val;
	sz[y] -= st[top].sz;
	fa[x] = st[top].fa;
	flag[x] = st[top].fx;
	flag[y] = st[top].fy;
	--top;
}
void dfs(int now,int fath){
	merge(a[now],b[now]);
	ans[now] = tmp;
	for(int i=head[now]; i; i=e[i].nxt){
		int to = e[i].to;
		if(to == fath)	continue;
		dfs(to,now);
	}
	del();
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n;scanf("%d",&n);
	for(int i=1; i<=n; i++)	scanf("%d%d",&a[i],&b[i]);
	for(int i=1; i<n; i++){
		int from,to;scanf("%d%d",&from,&to);
		add_edge(from,to);add_edge(to,from);
	}
	for(int i=1; i<=n; i++)	fa[i] = i,sz[i] = 1,flag[i] = true;
	dfs(1,0);
	for(int i=2; i<=n; i++)	printf("%d ",ans[i]);
	return 0;
}

标签:nxt,val,int,题解,top,ABC302,flag,Ex,now
From: https://www.cnblogs.com/linyihdfj/p/17418733.html

相关文章

  • 对$nextTick的理解,及其实现原理
    1.对$nextTick的理解:VUE中数据变化后,是异步更新DOM的,如果想数据变化后,操作dom,这个时候获取到的是没有变化的值eg:<divclass="msg">{{msg}}</div>mounted(){this.msg='我是测试文字'console.log(document.querySelector('.msg'......
  • NOIP2018普及组试题题解
    1.标题统计原题:https://www.luogu.com.cn/problem/P5015#include<bits/stdc++.h>#definelllonglongusingnamespacestd;strings;intans=0;intmain(){ getline(cin,s);intlen=s.length(); for(inti=0;i<len;i++){ if(s[i]>='0'&&......
  • #球钟算法题解以及代码完成
    球钟问题描述:球钟是一个利用球的移动来记录时间的简单装置。它有三个可以容纳若干个球的指示器:分钟指示器,五分钟指示器,小时指示器。若分钟指示器中有2个球,5分钟指示器中有6个球,小时指示器中有5个球,则时间为5:32。       工作原理:每过一分钟,球钟就会从球队列的队首......
  • 是什么让你的ExtJS应用程序运行缓慢?
          本文说的“缓慢”,是只运行时的缓慢,而不是只加载资源的时间。      在过去的一年半以来,我一直与RobertBosch在Bosch软件创新公司工作,在那里我们的前端技术堆栈非常依赖ExtJS。我有机会开发VisualRulesWebModeler机器协助开发其它几个基于ExtJS的应用,因此,我......
  • ExtJS 4中动态加载的路径设置
         在此首先感谢的文顺网友,是他提醒了我需要写这文的。     在Loader对象中,动态加载是使用getPath方法获取下载路径的,其代码如下:1 getPath: function(className) {2     var path = '',3         paths =......
  • Ext JS 4:模型剖析
          如果你在跟踪ExtJS动态,你可能已经知道,在ExtJS4中有一个全新的数据包。新的数据包在ExtJS3的基础上,增加了大良的新功能。近期我们在博客上介绍了新的数据包,今天我们将深度探讨新的Model类。     几乎每一个Model类就代表了应用程序中持久化的数据类型。例如......
  • Asp.net MVC 3实例学习之ExtShop(六)——登录对话框
         登录对话框将使用jquery提供的对话框,所以不需要添加其它js文件。首先要为登录对话框添加一个表单模型。在Models目录下创建一个“AccountModels”类文件,然后添加一个Logon类,代码如下:1     public class LogOnModel2     {3      ......
  • 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(1)
    应用程序的基本框架已经搭建好了,现在要做的是完成一个个的功能模块。先从简单做起,完成用户管理模块,该模块主要功能是使用一个Grid显示用户信息,并使用RowEditing进行用户的编辑、添加操作。Grid的分页则在Grid顶部使用分页工具条实现,在工具条上还要添加3个按钮用来添加用户、删除用......
  • 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(4)
    现在来完成删除功能。目前的Grid,一次只能选择一行,也就是说,一次只能删除一行,不太方便,因而要设置成使用复选框选择,并允许多选的。在用户视图脚本文件中,添加以下配置项实现这个:"checkboxmodel",false,mode:"MULTI" 打开页面浏览,会看到如图29所示的效果,已经可以在最左边通过复选框进......
  • 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之用户管理(3)
    昨天还有一个错误,CheckColumn的样式和图片没复制过来,造成最后一列的Checkbox显示不正确。在ExtJS包的examples\ux\css目录下打开CheckHeader.css文件,将文件里的全部样式定义复制到app.css中。然后修改将带背景图片的路径修改为“../images”。最后将image目录下的check.gif和unche......